B этом сценарии поток с более низким приоритетом вытесняется готовым к выполнению потоком с более высоким приоритетом. Такая ситуация может быть следствием двух обстоятельств:
• завершилось ожидание потока с более высоким приоритетом (т. е. произошло событие, которого он ждал);
• приоритет потока увеличился или уменьшился.
B любом из этих случаев Windows решает, продолжить выполнение текущего потока или вытеснить его потоком с более высоким приоритетом.
ПРИМЕЧАНИЕ Потоки пользовательского режима могут вытеснять потоки режима ядра. To есть режим выполнения потока значения не имеет — определяющим фактором является его приоритет.
Когда поток вытесняется, он помещается в начало очереди готовых потоков соответствующего уровня приоритета. Эту ситуацию иллюстрирует рис. 6-20.
Ha рис. 6-20 поток с приоритетом 18 выходит из состояния ожидания и вновь захватывает процессор, вытесняя выполняемый в этот момент поток (с приоритетом 16) в очередь готовых потоков. Заметьте, что вытесненный поток помещается не в конец, а в начало очереди. После завершения вытеснившего потока вытесненный сможет отработать остаток своего кванта.
Завершение кванта
Когда поток израсходует свой квант процессорного времени, Windows должна решить, следует ли понизить его приоритет и подключить к процессору другой поток.
Снизив приоритет потока, Windows ищет более подходящий для выполнения поток (таким потоком, например, будет любой из очереди готовых потоков с приоритетом выше нового приоритета текущего потока). Если Windows оставляет приоритет потока прежним и в очереди готовых потоков есть другие потоки с тем же приоритетом, она выбирает из очереди следующий поток с тем же приоритетом, а выполнявшийся до этого поток перемещает в хвост очереди (задавая ему новую величину кванта и переводя его из состояния Running в состояние Ready). Этот случай иллюстрирует рис. 6-21. Если ни один поток с тем же приоритетом не готов к выполнению, текущему потоку выделяется еще один квант процессорного времени.
Завершение потока
Завершаясь (после возврата из основной процедуры и вызова ExitThread или из-за уничтожения вызовом TerminateThread), поток переходит в состояние Terminated. Если в этот момент ни один описатель его объекта «поток» не открыт, поток удаляется из списка потоков процесса, и соответствующие структуры данных освобождаются.
Переключение контекста
Контекст потока и процедура его переключения зависят от архитектуры процессора. B типичном случае переключение контекста требует сохранения и восстановления следующих данных:
• указателя команд;
• указателей на стек ядра и пользовательский стек;
• указателя на адресное пространство, в котором выполняется поток (каталог таблиц страниц процесса).
Ядро сохраняет эту информацию, заталкивая ее в текущий стек ядра, обновляя указатель стека и сохраняя его в блоке KTHREAD потока. Далее указатель стека ядра устанавливается на стек ядра нового потока и загружается контекст этого потока. Если новый поток принадлежит другому процессу, в специальный регистр процессора загружается адрес его каталога таблиц страниц, в результате чего адресное пространство этого процесса становится доступным (о трансляции адресов см. в главе 7). При наличии отложенной APC ядра запрашивается прерывание IRQL уровня 1. B ином случае управление передается загруженному для нового потока указателю команд, и выполнение этого потока возобновляется.
Поток простоя
Если нет ни одного потока, готового к выполнению на процессоре, Windows подключает к данному процессору поток простоя (процесса Idle). Для каждого процессора создается свой поток простоя.
Разные утилиты для просмотра процессов в Windows по-разному называют процесс Idle. Диспетчер задач и Process Explorer обозначают его как «System Idle Process», Process Viewer — как «Idle», Pstat — как «Idle Process», Process Explode и Tlist — как «System Process», a Qslice — как «SystemProcess». Windows сообщает, что приоритет потока простоя равен 0. Ho на самом деле у него вообще нет уровня приоритета, поскольку он выполняется лишь в отсутствие других потоков. (Вспомните, что на нулевом уровне приоритета в Windows работает лишь поток обнуления страниц; см. главу 7.)
Холостой цикл, работающий при IRQL уровня «DPC/dispatch», просто запрашивает задания, например на доставку отложенных DPC или на поиск потоков, подлежащих диспетчеризации.
Хотя последовательность работы потока простоя зависит от архитектуры, он все равно выполняет следующие действия.
1. Включает и отключает прерывания (тем самым давая возможность доставить отложенные прерывания).
2. Проверяет, нет ли у процессора незавершенных DPC (см. главу 3). Если таковые есть, сбрасывает отложенное программное прерывание и доставляет эти DPC
3. Проверяет, выбран ли какой-нибудь поток для выполнения на данном процессоре, и, если да, организует его диспетчеризацию.
4. Вызывает из HAL процедуру обработки процессора в простое (если нужно выполнить какие-либо функции управления электропитанием).
B Windows Server 2003 поток простоя также проверяет наличие потоков, ожидающих выполнения на других процессорах, но об этом пойдет речь в разделе по планированию потоков в многопроцессорных системах.
Динамическое повышение приоритета
Windows может динамически повышать значение текущего приоритета потока в одном из пяти случаев:
• после завершения операций ввода-вывода;
• по окончании ожидания на событии или семафоре исполнительной системы;
• по окончании операции ожидания потоками активного процесса;
• при пробуждении GUI-потоков из-за операций с окнами;
• если поток, готовый к выполнению, задерживается из-за нехватки процессорного времени.
Динамическое повышение приоритета предназначено для оптимизации общей пропускной способности и отзывчивости системы, а также для устранения потенциально «нечестных» сценариев планирования. Однако, как и любой другой алгоритм планирования, динамическое повышение приоритета — не панацея, и от него выигрывают не все приложения.
ПРИМЕЧАНИЕ Windows никогда не увеличивает приоритет потоков в диапазоне реального времени (16–31). Поэтому планирование таких потоков по отношению к другим всегда предсказуемо. Windows считает: тот, кто использует приоритеты реального времени, знает, что делает.
Динамическое повышение приоритета после завершения ввода-вывода
Windows временно повышает приоритет потоков по окончании определенных операций ввода-вывода, поэтому у потоков, ожидавших завершения таких операций, больше шансов немедленно возобновить выполнение и обработать полученные данные. Вспомните: после пробуждения потока оставшийся у него квант уменьшается на одну единицу, так что потоки, ожидавшие завершения ввода-вывода, не получают неоправданных преимуществ. Хотя рекомендованные приращения в результате динамического повышения приоритета определены в заголовочных файлах DDK (ищите строки «#define IO» в Wdm.h или Ntddk.h; эти же значения перечислены в таблице 6-17), реальное приращение определяется драйвером устройства. Именно драйвер устройства указывает — через функцию ядра IoCompleteRequest — на необходимость динамического повышения приоритета после выполнения запроса на ввод-вывод. Заметьте, что для запросов на ввод-вывод, адресованных устройствам, которые гарантируют меньшее время отклика, предусматриваются большие приращения приоритета.
Приоритет потока всегда повышается относительно базового, а не текущего уровня. Как показано на рис. 6-22, после динамического повышения приоритета поток в течение одного кванта выполняется с повышенным уровнем приоритета, после чего приоритет снижается на один уровень и потоку выделяется еще один квант. Этот цикл продолжается до тех пор, пока приоритет не снизится до базового. Поток с более высоким приоритетом все равно может вытеснить поток с повышенным приоритетом, но прерванный поток должен полностью отработать свой квант с повышенным приоритетом до того, как этот приоритет начнет понижаться.
Как уже отмечалось, динамическое повышение приоритета применяется только к потокам с приоритетом динамического диапазона (0-15). Независимо от приращения приоритет потока никогда не будет больше 15. Иначе говоря, если к потоку с приоритетом 14 применить динамическое повышение на 5 уровней, его приоритет возрастет лишь до 15. Если приоритет потока равен 15, он остается неизменным при любой попытке его повышения.