Новые потоки выделяются зеленым, а существующие – красным. (Длительность подсветки настраивается в Options.) Это помогает обнаруживать создание лишних потоков в процессе. (Как правило, потоки должны создаваться при запуске процесса, а не при каждой обработке какого-либо запроса внутри процесса.)
   Когда вы поочередно выбираете потоки в списке, Process Explorer отображает их идентификаторы, время запуска, состояние, счетчики использования процессорного времени, число переключений контекстов, а также базовый и текущий приоритеты. Кнопка KiIl позволяет принудительно завершать индивидуальные потоки, но пользоваться ею следует с крайней осторожностью.
   Разница в числе переключений контекста (context switch delta) отражает, сколько раз потоки начинали работать в течение периода обновления, указанного в Process Explorer. Это еще один способ определения активности потоков. B некоторых отношениях он даже лучше, так как многие потоки выполняются в течение лишь очень короткого времени и поэтому крайне редко попадают в список текущих потоков. Например, если вы добавите столбец с разницей в числе переключений контекстов к тому, что показывается для процесса и отсортируете по этому столбцу, то увидите процессы, в которых потоки выполняются, но используют очень мало процессорного времени или вообще его не используют.
   Стартовый адрес потока выводится в виде «module\function»,где module –имя EXE или DLL. Имя функции извлекается из файла символов для данного модуля (см. эксперимент «Просмотр детальных сведений о процессах с помощью Process Explorer» в главе 1). Если вы точно не знаете, что это за модуль, нажмите кнопку Module, и появится окно свойств модуля, где содержится данная функция.
 
    ПРИМЕЧАНИЕ Для потоков, созданных 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), которая обратилась к функции OpenPrinterWBWinspool.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 не станет поровну делить процессорное время между двумя процессами.
   Чтобы понять алгоритмы планирования потоков, вы должны сначала разобраться в уровнях приоритета, используемых Windows.
 
Уровни приоритета
   Как показано на рис. 6-11, в Windows предусмотрено 32 уровня приоритета – от 0 до 31. Эти значения группируются так: (o)шестнадцать уровней реального времени (16-31); (o)пятнадцать варьируемых (динамических) уровней (1-15);
 
    (o)один системный уровень (0), зарезервированный для потока обнуления страниц (zero page thread).
   Уровни приоритета потока назначаются с учетом двух разных точек зрения – Windows API и ядра Windows. Windows API сначала упорядочивает процессы по классам приоритета, назначенным при их создании [Real-time (реального времени), High (высокий), Above Normal (выше обычного), Normal (обычный), Below Normal (ниже обычного) и IdIe (простаивающий)], а затем – по относительному приоритету индивидуальных потоков в рамках этих процессов [Time-critical (критичный по времени), Highest (наивысший), Above-normal (выше обычного), Normal (обычный), Below-normal (ниже обычного), Lowest (наименьший) и IdIe (простаивающий)].
   Базовый приоритет каждого потока в Windows API устанавливается, исходя из класса приоритета его процесса и относительного приоритета самого потока. Связь между приоритетами Windows API и внутренними приоритетами ядра Windows (в числовой форме) показана на рис. 6-12.
   Если у процесса только одно значение приоритета (базовое), то у каждого потока их два: текущее и базовое. Решения, связанные с планированием, принимаются на основе текущего приоритета. Как поясняется в следующем разделе, в определенных обстоятельствах система может на короткое время повышать приоритеты потоков в динамическом диапазоне (1-15). Windows никогда не изменяет приоритеты потоков в диапазоне реального времени (16-31 ),поэтому у таких потоков базовый приоритет идентичен текущему.
    Рис. 6-12. Взаимосвязь приоритетов в ядре и Windows API
 
   Начальный базовый приоритет потока наследуется от базового приоритета процесса, а тот наследует его от родительского процесса. Это поведение можно изменить при вызове Windows-функции CreateProcess иликоманды START. Приоритет процесса можно изменить и после его создания, используя функцию SetPriorityClassили различные утилиты, предоставляющие доступ к этой функции через UI, например диспетчер задач и Process Explorer. B частности, вы можете понизить приоритет процесса, интенсивно использующего процессорное время, чтобы он не мешал обычным операциям в системе. Смена приоритета процесса влечет за собой смену приоритетов всех его потоков, но их относительные приоритеты остаются прежними. Ho изменение приоритетов индивидуальных потоков внутри процесса обычно не имеет смысла, потому что вы не знаете, чем именно занимается каждый из них (если только сами не пишете программу или не располагаете исходным кодом); так что изменение относительных приоритетов потоков может привести к неадекватному поведению этого приложения.
   Обычно базовый приоритет процесса (а значит, и базовый приоритет первичного потока) по умолчанию равен значению из середины диапазонов приоритетов процессов (24, 13, 10, 8, 6 или 4). Однако базовый приоритет некоторых системных процессов (например, диспетчера сеансов, контроллера сервисов и сервера локальной аутентификации) несколько превышает значение по умолчанию для класса Normal (8). Более высокий базовый приоритет по умолчанию обеспечивает запуск потоков этих процессов с приоритетом выше 8. Чтобы изменить свой начальный базовый приоритет, такие системные процессы используют внутреннюю функцию NtSetInformationProcess.
 
Функции Windows API, связанные с планированием
   Эти функции перечислены в таблице 6-14 (более подробную информацию см. в справочной документации Windows API).
 
    Таблица 6-14. API-функции планирования и их назначение
 
Сопутствующие утилиты
   B следующей таблице перечислены утилиты, сообщающие информацию о планировании потоков. Базовый приоритет процесса можно увидеть (и изменить) с помощью диспетчера задач, Process Explorer, Pview или Pviewer. Заметьте, что Process Explorer позволяет уничтожать отдельные потоки в любых процессах. Ho, конечно же, этой возможностью следует пользоваться с крайней осторожностью.
   Приоритеты потоков можно просмотреть в оснастке Performance (Производительность), а также с помощью утилит Process Explorer, Plist, Pview, Pviewer и Pstat. Хотя повышение или понижение приоритета процесса может оказаться весьма полезным, изменение приоритетов индивидуальных потоков внутри процесса, как правило, не имеет смысла, потому что постороннему человеку не известно, что делают эти потоки и почему важны именно такие их относительные приоритеты.
   Единственный способ задать начальный класс приоритета для процесса – использовать команду startв командной строке Windows. Если вы хотите, чтобы некая программа каждый раз запускалась с определенным приоритетом, то можете создать для нее ярлык и указать команду запуска, предварив ее cmd /с.Это приведет к появлению окна командной строки, выполнению команды и последующему закрытию этого окна. Например, чтобы запустить Notepad в процессе с низким приоритетом, в свойствах ярлыка должна быть задана команда cmd /с start /low notepad.exe.
 
    ЭКСПЕРИМЕНТ: исследуем и задаем приоритеты процессов и потоков
   Попробуйте провести такой эксперимент.
   1. Наберите в командной строке start /realtime notepad.Ha экране появится окно Notepad.
   2. Запустите утилиту Process Explorer или Process Viewer (Pviewer.exe) из Support Tools и выберите Notepad.exe из списка процессов, как показано ниже. Заметьте, что динамический приоритет потока Notepad равен 24. Это значение совпадает со значением приоритета реального времени на рис. 6-12.
   3. Аналогичную информацию можно получить в диспетчере задач. Для его запуска нажмите клавиши Ctrl+Shift+Esc и перейдите на вкладку Processes (Процессы). Щелкните правой кнопкой мыши процесс Notepad.exe и выберите команду Set Priority (Приоритет). Вы увидите, что класс приоритета потока относится к Realtime (Реального времени), как показано на следующей иллюстрации.
 
Диспетчер системных ресурсов Windows
   B Windows Server 2003 Enterprise Edition и Windows Server 2003 Data-center Edition включен необязательный компонент, который называется диспетчером системных ресурсов Windows (Windows System Resource Manager, WSRM). Он позволяет администратору настраивать правила политики, указывающие для процессов использование процессорного времени, параметры привязки к процессорам и лимиты на физическую и виртуальную память. Кроме того, WSRM может генерировать отчеты по использованию ресурсов, удобные для учета и проверки уровня обслуживания по договорам с пользователями.
   Такие правила могут быть применены к конкретным приложениям, пользователям или группам и действовать в определенные периоды или постоянно.
   После того как вы сформировали политику выделения ресурсов для управления определенными процессами, служба WSRM будет вести мониторинг потребления ими процессорного времени и регулировать их базовые приоритеты, если эти процессы будут использовать процессорного времени больше или меньше, чем было установлено вами.
   Ограничение физической памяти достигается заданием максимального размера рабочего набора через функцию SetProcessWorkingSetSizeEx,а ограничение виртуальной памяти реализуется самой службой (о лимитах на объемы физической и виртуальной памяти см. главу 7). Если заданный лимит превышен, WSRM – в зависимости от настроек – может уничтожать процессы или создавать соответствующую запись в журнале событий. Последнее позволяет выявить процесс с утечкой памяти до того, как он займет всю переданную виртуальную память в системе. Заметьте, что лимиты на память, установленные в WSRM, не применяются к памяти Address Windowing Extensions (AWE), памяти больших страниц (large page memory) или памяти ядра (пулу подкачиваемых или неподкачиваемых страниц).
 
Приоритеты реального времени
   Вы можете повысить или понизить приоритет потока любого приложения в динамическом диапазоне; однако, чтобы задать значение из диапазона реального времени, у вас должна быть привилегия Increase Scheduling Priority. Учтите, что многие важные системные потоки режима ядра выполняются в диапазоне приоритетов реального времени. Поэтому, если потоки слишком долго выполняются с приоритетом этого диапазона, они могут блокировать критичные системные функции (например в диспетчере памяти, диспетчере кэша или драйверах устройств).
 
    ПРИМЕЧАНИЕ Как показано на следующей иллюстрации, где изображены уровни запросов прерываний (Interrupt Request Levels, IRQL) на платформе x86, в Windows имеется набор приоритетов, называемых приоритетами реального времени, но они не являются таковыми в общепринятом смысле этого термина, так как Windows не относится к операционным системам реального времени. Подробнее на эту тему см. врезку «Windows и обработка данных в реальном времени» в главе 3, а также статью «Real-Time Systems and Microsoft Windows NT» в MSDN Library.
 
Уровни прерываний и уровни приоритета
   Как показано на следующей иллюстрации, потоки обычно выполняются при IRQL, равном 0 или 1. (Описание уровней прерываний в Windows см. в главе 3.) Потоки пользовательского режима всегда выполняются при IRQL, равном 0. Ввиду этого ни один поток пользовательского режима независимо от его приоритета не в состоянии блокировать аппаратные прерывания (хотя потоки с высоким приоритетом из диапазона реального времени способны блокировать важные системные потоки). При IRQL, равном 1, работают только APC режима ядра, поскольку они прерывают выполнение потоков (об APC см. главу 3). Кроме того, потоки, выполняемые в режиме ядра, могут повышать IRQL, например при обработке системного вызова, требующего диспетчеризации потоков.
 
Состояния потоков
 
   Прежде чем перейти к алгоритмам планирования потоков, вы должны разобраться, в каких состояниях могут находиться потоки в процессе выполнения в Windows 2000 и Windows XR Соответствующая схема дана на рис. 6-13 [числовые значения отражают показатели счетчика производительности Thread: thread state (Поток-. Состояние потока)].
   Вот что представляют собой состояния потока.
    (o) Ready (готов)Поток в состоянии готовности ожидает выполнения. Выбирая следующий поток для выполнения, диспетчер принимает во внимание только пул потоков, готовых к выполнению.
    (o) Standby (простаивает)Поток в этом состоянии уже выбран следующим для выполнения на конкретном процессоре. B подходящий момент диспетчер переключает контекст на этот поток. B состоянии Standby может находиться только один поток для каждого процессора в системе. Заметьте, что поток может быть вытеснен даже в этом состоянии (если, например, до начала выполнения потока, который пока находится в состоянии Standby, к выполнению будет готов поток с более высоким приоритетом).
    (o) Running (выполняется)Поток переходит в это состояние и начинает выполняться сразу после того, как диспетчер переключает на него контекст. Выполнение потока прекращается, как только он завершается, вытесняется потоком с более высоким приоритетом, переключает контекст на другой поток, самостоятельно переходит в состояние ожидания или истекает выделенный ему квант процессорного времени (и другой поток с тем же приоритетом готов к выполнению).
    (o) Waiting (ожидает)Поток входит в состояние Waiting несколькими способами. Он может самостоятельно начать ожидание на синхронизирующем объекте или его вынуждает к этому подсистема окружения. По окончании ожидания поток – в зависимости от приоритета – либо немедленно начинает выполняться, либо переходит в состояние Ready.
    (o) Transition (переходное состояние)Поток переходит в это состояние, если он готов к выполнению, но его стек ядра выгружен из памяти. Как только этот стек загружается в память, поток переходит в состояние Ready.
    (o) Terminated (завершен)Заканчивая выполнение, поток переходит в состояние Terminated. После этого блок потока исполнительной системы (структура данных в пуле неподкачиваемой памяти, описывающая данный поток) может быть удален, а может быть и не удален – это уже определяется диспетчером объектов.
    (o) Initialized (инициализирован)B это состояние поток входит в процессе своего создания.
 
    ЭКСПЕРИМЕНТ: изменение состояний потоков при планировании
   Вы можете понаблюдать за изменением этих состояний с помощью оснастки Performance. Она может оказаться полезной в отладке многопоточных приложений, если вам нужно проверить состояние потоков, выполняемых в вашем процессе.
   1. Запустите стандартную программу Notepad (Блокнот) (Notepad.exe).
   2. Запустите оснастку Performance (Производительность), открыв в меню Start (Пуск) подменю Programs (Программы) и Administrative Tools (Администрирование), а затем выбрав команду Performance (Производительность).
   3. Выберите режим просмотра диаграмм (если установлен какой-то другой).
   4. Щелкните график правой кнопкой мыши и выберите команду Properties (Свойства).
   5. Откройте вкладку Graph (График) и установите максимальное значение вертикальной шкалы равным 7. (Состояниям потоков соответствуют числа от O до 7). Щелкните кнопку ОК.
   6. Щелкните на панели инструментов кнопку Add (Добавить), чтобы открыть диалоговое окно Add Counters (Добавить счетчики).
   7. Выберите в списке объект Thread (Поток), а затем – счетчик Thread State (Состояние потока). Определение его значений вы увидите, щелкнув кнопку Explain (Объяснение), как показано ниже.
   8. Прокрутите список вхождений до строки notepad/O (это процесс Notepad), выделите его и щелкните кнопку Add (Добавить).
   9. Прокрутите список назад до процесса Mmc (это процесс Microsoft Management Console, в котором выполняется ActiveX-элемент System Monitor), выберите все его потоки (mmc/0, mmc/1 и т.д.) и добавьте их на график, щелкнув кнопку Add. Прежде чем щелкнуть кнопку Add, вы должны увидеть диалоговое окно, аналогичное показанному ниже.
   10. Теперь закройте диалоговое окно Add Counters, щелкнув кнопку Close (Закрыть).
   11. Вы должны увидеть, что поток Notepad (верхняя линия графика) находится в состоянии 5. Как вы уже знаете, значение 5 соответствует состоянию Waiting. (B данном случае поток ждет GUI-ввода.)
   12.3аметьте, что один из потоков процесса Mmc (выполняющий оснастку Performance) находится в состоянии Running (значение 2). Этот поток всегда выполняется, так как постоянно запрашивает состояние других потоков.
   13. Вы никогда не увидите процесс Notepad в состоянии Running (если только не используете многопроцессорную систему), поскольку в этом состоянии всегда находится Mmc, собирая данные о состоянии отслеживаемых потоков.
   Схема состояний потоков в Windows Server 2003 показана на рис. 6-14. Обратите внимание на новое состояние Deferred Ready (готов, отложен). Это состояние используется для потоков, выбранных для выполнения на конкретном процессоре, но пока не запланированных к выполнению. Это новое состояние предназначено для того, чтобы ядро могло свести к минимуму срок применения общесистемной блокировки к базе данных планирования (scheduling database). (Этот процесс подробно описывается в разделе «База данных диспетчера ядра в многопроцессорной системе».)
 
База данных диспетчера ядра
   Для принятия решений при планировании потоков ядро поддерживает набор структур данных, в совокупности известных как база данных ducnem-чераядра(dispatcher database) (рис. 6-15). Эта база данных позволяет отслеживать потоки, ждущие выполнения, и потоки, выполняемые на тех или иных процессорах.
 
    ПРИМЕЧАНИЕ База данных диспетчера ядра в однопроцессорной системе имеет ту же структуру, что и в многопроцессорных системах Windows 2000 и Windows XP, но отличается от структуры такой базы данных в системах Windows Server 2003. Эти различия, а также иной алгоритм выбора потоков для выполнения в многопроцессорных системах поясняются в разделе «Многопроцессорные системы».
 
    Очереди готовых потоков(ready queues) диспетчера ядра включают потоки в состоянии Ready, ожидающие выделения им процессорного времени. Для каждого из 32 уровней приоритета существует по одной очереди. Для ускорения выбора потока, подлежащего выполнению или вытеснению, Windows поддерживает 32-битную маску, называемую