Действия, выполняемые функцией scheduled для переключения процессов
Итак, функция schedule о определилась, какой процесс следует выполнять дальше. Теперь ядро обратится к структуре thread info процесса next, адрес которой хранится почти в самом начале дескриптора процесса next:
switch_tasks: prefetch(next);
Макрос prefetch заставляет управляющий блок процессора перенести содержимое первых полей дескриптора процесса next в аппаратный кэш. Он вызывается здесь ДЛЯ повышения производительности функции schedule о, потому что данные будут переноситься одновременно с выполнением инструкций, идущих следом и не затрагивающих поля дескриптора next.
Перед замещением процесса prev планировщик должен проделать определенную организационную работу:
clear_tsk_need_resched(prev); rcu_qsctr_inc(prev->thread_info->cpu);
Функция ciear_tsk_need_resched сбрасывает флаг tif_need_resched у процесса prev на случай, если функция schedule была вызвана "ленивым" образом. Затем функция фиксирует тот факт, что процессор временно пребывает в состоянии покоя
Функция schedule о должна, кроме прочего, уменьшить среднее время сна процесса prev, вычтя из него то время, что процесс пользовался процессором:
prev->sleep_avg -= run_time; if ((long)prev->sleep_avg sleep_avg = 0; prev->timestamp = prev->last_ran = now;
Затем у процесса обновляются отметки времени.
Вполне ВОЗМОЖНО, ЧТО prev и next являются одним и тем же процессом. Это происходит, когда в очереди на ожидание нет другого активного процесса
с более высоким или равным приоритетом. В таком случае функция обходит переключение процесса:
if (prev == next) {
spin_unlock_irq(&rq->lock); goto finish_schedule;
}
Теперь планировщик находится в такой точке кода, что prev и next являются разными процессами, и переключение необходимо:
next->timestamp = now; rq->nr_switches++; rq->curr = next;
prev = context_switch(rq, prev, next);
Функция context switcho настраивает адресное пространство процесса next. Как мы увидим в разд. "Дескриптор памяти для потоков ядра" в главе 9, поле active mm дескриптора процесса указывает на дескриптор памяти, используемой процессом, а поле mm— на дескриптор памяти, принадлежащей процессу. У обычных процессов в этих полях находится один адрес, однако, у потока ядра нет собственного адресного пространства, и его поле mm всегда содержит null. Функция context switcho делает так, что если next является потоком ядра, то он использует адресное пространство процесса prev:
if (!next->mm) {next->active_mm = prev->active_mm; atomic_inc (&prev->active_mm->mm_count) ; enter_lazy_tlb(prev->active_mm, next);}
Вплоть до версии Linux потоки ядра имели свои адресные пространства. Это решение не было оптимальным, поскольку приходилось менять Таблицы Страниц каждый раз, когда планировщик выбирал новый процесс, даже если это был поток ядра. Поскольку потоки ядра работают в режиме ядра, они используют только четвертый гигабайт пространства линейных адресов, который отображается одинаково для всех процессов в системе. Но еще хуже то, что запись в регистр сгз делает недействительными все данные в TLB- буферах а это приводит к значительному снижению производительности. Сейчас операционная система Linux гораздо эффективнее, поскольку Таблицы Страниц вообще не затрагиваются, если процесс next является потоком ядра. В порядке дальнейшей оптимизации, если процесс next является потоком ядра, функция schedule о переводит процесс в ленивый режим TLB.
Если же next является обычным процессом, функция context switcho заменяет адресное пространство процесса prev адресным процессом процесса
next:
if (next->mm)
switch_mm(prev->active_mm, next->mm, next);
Если процесс prev является потоком ядра или завершает свою работу, функция context switcho сохраняет указатель на дескриптор памяти, используемой процессом prev, в поле prev mm структуры, определяющей очередь на выполнение, а затем сбрасывает поле prev->active_mm:
if (!prev->mm) {
rq->prev_mm = prev->active_mm; prev->active_mm = NULL;
}
Теперь функция contextswitch о может наконец-то вызвать макрос switch toO для фактического переключения между процессами prev и next:
switch_to(prev, next, prev); return prev;
Предыдущая страница | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | Следующая страница