ПРИМЕЧАНИЕ Для потоков, созданных Windows-функцией Create-Tbread, Process Explorer показывает функцию, переданную в Create-Tbread, а не истинную стартовую функцию потока. Это связано с тем, что все Windows-потоки запускаются в общей стартовой функции- оболочке для процессов или потоков (BaseProcessStartJin6oBaseThre-adStart в Kernel32.dll). Если бы Process Explorer выводил истинный стартовый адрес, то казалось бы, что большинство потоков в процессе были запущены по одному адресу, а это вряд ли помогло бы понять, какой код выполняется потоком.
Однако одного стартового адреса потока может оказаться недостаточно для того, чтобы выяснить, что именно делает поток и какой компонент внутри процесса отвечает за использование процессорного времени этим потоком. Это особенно верно, если стартовый адрес потока относится к универсальной стартовой функции (например, если имя функции не указывает на
то, что делает данный поток). Тогда может помочь изучение стека потока. Для его просмотра дважды щелкните интересующий вас поток (или выберите этот поток и нажмите кнопку Stack). Process Explorer покажет стек потока (пользовательского режима и режима ядра, если поток был в последнем режиме).
ПРИМЕЧАНИЕ Отладчики пользовательского режима (Windbg, Ntsd и Cdb) тоже позволяют подключаться к процессу и просматривать стек потока, но Process Explorer выводит стек как пользовательского режима, так и режима ядра простым нажатием одной кнопки. Стеки пользовательского режима и режима ядра можно, но эта утилита гораздо сложнее в использовании. Кстати, при работе Windbg в режиме локальной отладки ядра, поддерживаемом только в Windows XP и Windows Server 2003, увидеть содержимое стеков потоков нельзя.
Просмотр стека потока полезен и при поиске причины зависания процесса. Например, на одной системе Microsoft PowerPoint зависал при запуске на минуту. Чтобы понять причину этого зависания, с помощью Process Explorer изучили стек одного из потоков в процессе. Результат приведен на рис. 6-10.
Как видите, PowerPoint (строка 10) вызвал функцию в Mso.dll (основной Microsoft Office DLL), которая обратилась к функции OpenPrinterWB Winspool.drv (DLL, используемой для подключения к принтерам). Затем Winspool.drv пересылает запрос функции OpenPrinterRPC, а та вызывает функцию в DLL исполняющей среды RPC, сообщая, что запрос посылается удаленному принтеру. Вот так, не зная деталей внутреннего устройства PowerPoint, по именам модулей и функций в стеке потока можно понять, что поток ждет соединения с сетевым принтером. B данной системе был сетевой принтер, который не отвечал, что и объясняет задержку в запуске PowerPoint. (Приложения Microsoft Office соединяются со всеми сконфигурированными принтерами при запуске.) Соединение с тем принтером было удалено из пользовательской системы, и проблема исчезла.
Планирование потоков Здесь описываются стратегии и алгоритмы планирования в Windows. B первом разделе этой части материалов рассматриваются принципы планирования в Windows и даются определения ключевых терминов. Уровни приоритета обсуждаются с точки зрения как Windows API, так и ядра. После обзора сопутствующих Windows-функций и утилит подробно анализируются – сначала в однопроцессорных системах, а затем и в многопроцессорных – алгоритмы и структуры данных, используемые подсистемой планирования Windows.
Обзор планирования в Windows B Windows реализована подсистема вытесняющего планирования на основе уровней приоритета, в которой всегда выполняется поток с наибольшим приоритетом, готовый к выполнению. Однако выбор потока для выполнения может быть ограничен набором процессоров, на которых он может работать. Это явление называется привязкой к процессорам (processor affinity). По умолчанию поток выполняется на любом доступном процессоре, но вы можете изменить привязку к процессорам через Windows-функции планирования, перечисленные в таблице 6-14 (см. далее в этой главе), или заданием маски привязки в заголовке образа.
ЭКСПЕРИМЕНТ: просмотр потоков, готовых к выполнению
Список потоков, готовых к выполнению, можно увидеть с помощью команды !ready отладчика ядра. Она выводит поток или список потоков, готовых к выполнению (на каждом уровне приоритета отдельно). B следующем примере к выполнению готовы два потока с приоритетом 10 и шесть потоков – с приоритетом 8. Поскольку эта информация получена в однопроцессорной системе с использованием LiveKd, текущим потоком всегда является отладчик ядра (Kd или WinDbg).
Выбранный для выполнения поток работает в течение некоего периода, называемого квантом. Квант определяет, сколько времени будет выполняться поток, пока не наступит очередь другого потока с тем же приоритетом (или более высоким, что возможно в многопроцессорной системе). Длительность квантов зависит от трех факторов: конфигурационных параметров системы (длинные или короткие кванты), статуса процесса (активный или фоновый) и использования объекта «задание» для изменения длительности квантов. (Подробнее о квантах см. раздел «Квант» далее в этой главе.) Однако поток может не полностью использовать свой квант. Поскольку в Windows реализован вытесняющий планировщик, то происходит вот что. Как только другой поток с более высоким приоритетом готов к выполнению, текущий поток вытесняется, даже если его квант еще не истек. Фактически поток может быть выбран следующим для выполнения и вытеснен, не успев воспользоваться своим квантом!
Код Windows, отвечающий за планирование, реализован в ядре. Поскольку этот код рассредоточен по ядру, единого модуля или процедуры с названием «планировщик» нет. Совокупность процедур, выполняющих эти обязанности, называется диспетчерам ядра (kernel's dispatcher). Диспетчеризация потоков может быть вызвана любым из следующих событий.
(o) Поток готов к выполнению – например, он только что создан или вышел из состояния ожидания.
(o) Поток выходит из состояния Running (выполняется), так как его квант истек или поток завершается либо переходит в состояние ожидания.
(o) Приоритет потока изменяется в результате вызова системного сервиса или самой Windows.
(o) Изменяется привязка к процессорам, из-за чего поток больше не может работать на процессоре, на котором он выполнялся.
B любом случае Windows должна определить, какой поток выполнять следующим. Выбрав новый поток, Windows переключает контекст. Эта операция заключается в сохранении параметров состояния машины, связанных с выполняемым потоком, и загрузке аналогичных параметров для другого потока, после чего начинается выполнение нового потока.
Как уже говорилось, планирование в Windows осуществляется на уровне потоков. Этот подход станет понятен, если вы вспомните, что сами процессы не выполняются, а лишь предоставляют ресурсы и контекст для выполнения потоков. Поскольку решения, принимаемые в ходе планирования, касаются исключительно потоков, система не обращает внимания на то, какому процессу принадлежит тот или иной поток. Так, если у процесса A есть 10, у процесса B – 2 готовых к выполнению потока, и все 12 имеют одинаковый приоритет, каждый из потоков теоретически получит 1/12 процессорного времени, потому что Windows не станет поровну делить процессорное время между двумя процессами.
Чтобы понять алгоритмы планирования потоков, вы должны сначала разобраться в уровнях приоритета,