K этому моменту выполнение всех процессов в сеансе уже завершено. Winlogon снова вызывает функцию ExitWindowsEx, на этот раз в контексте системного процесса, и та посылает Csrss сообщение. Csrss просматривает все процессы, принадлежащие контексту системы и рассылает сообщения WM_QUERYENDSESSION/WM_ENDSESSION всем GUI-потокам. Ho консольным приложениям с зарегистрированными обработчиками Csrss посылает не CTRL_LOGOFF_EVENT, a CTRL_SHUTDOWN_EVENT. Заметьте, что SCM является консольной программой, которой регистрируется свой обработчик. Получив запрос на завершение, SCM рассылает соответствующие сообщения всем сервисам, которые зарегистрированы на уведомление о завершении работы. Подробнее о завершении работы сервисов (в том числе о таймауте для SCM) см. раздел «Сервисы» главы 4.
Хотя при завершении системных процессов действуют те же таймауты, что и для пользовательских процессов, Csrss не выводит никаких диалоговых окон и не завершает их принудительно. (Значения таймаутов завершения системных процессов берутся из профиля пользователя по умолчанию.) Смысл этих таймаутов только в том, чтобы системные процессы корректно завершились до выключения системы. Ho многие системные процессы вроде Smss, Winlogon, SCM и LSASS на самом деле еще выполняются при выключении системы.
Как только Csrss заканчивает рассылку уведомлений системным процессам о завершении работы, Winlogon вызывает функцию исполнительной системы NtShutdownSystem. Она в свою очередь вызывает функцию NtSet-SystemPowerState, управляющую завершением драйверов и остальных компонентов исполнительной системы (диспетчеров Plug and Play, электропитания, ввода-вывода, конфигурации и памяти).
Например, NtSetSystemPowerState вызывает диспетчер ввода-вывода для рассылки пакетов завершения ввода-вывода всем драйверам устройств, запросившим уведомление о завершении системы. Это позволяет драйверам подготовить свои устройства к завершению работы Windows. Диспетчер конфигурации сбрасывает на диск все измененные данные реестра, а диспетчер памяти записывает все измененные страницы с файловыми данными обратно в соответствующие файлы. Диспетчер памяти производит очистку страничного файла (если это указано в настройках). Далее вновь вызывается диспетчер ввода-вывода, который информирует драйверы файловой системы о завершении Windows. Процесс завершения работы заканчивается на диспетчере электропитания, дальнейшие действия которого зависят от пользовательских настроек (выключение компьютера, перезагрузка или переход в ждущий режим).
Резюме
B этой главе мы подробно исследовали процессы загрузки и завершения работы Windows в нормальном режиме и при возникновении сбоев. K этому моменту мы уже рассмотрели общую структуру Windows и базовые системные механизмы, обеспечивающие запуск, работу и в конечном счете выключение системы. Заложив такой фундамент, можно переходить к более подробному изучению отдельных компонентов исполнительной системы, начиная с процессов и потоков.
ГЛABA 6 Процессы, потоки и задания
B этой главе мы рассмотрим структуры данных и алгоритмы, связанные с процессами, потоками и заданиями в Microsoft Windows. B первом разделе основное внимание уделяется внутренним структурам данных, из которых состоит процесс. Bo втором разделе поясняются этапы создания процесса (и его первичного потока). Далее поясняются внутреннее устройство потоков и их планирование. Завершается глава описанием объекта «задание» Job object).
B процессе изложения материала мы будем упоминать соответствующие счетчики производительности или переменные ядра. Хотя программирование под Windows не является предметом этой книги, в ней перечисляются Windows-функции, связанные с процессами, потоками и заданиями, — это даст вам представление о том, как они используются.
Внутреннее устройство процессов
B этом разделе описываются ключевые структуры данных процессов Windows. Также поясняются основные переменные ядра, счетчики производительности, функции и утилиты, имеющие отношение к процессам.
Структуры данных
Каждый процесс в Windows представлен блоком процесса, создаваемым исполнительной системой (EPROCESS). Кроме многочисленных атрибутов, относящихся к процессу, в блоке EPROCESS содержатся указатели на некоторые структуры данных. Так, у каждого процесса есть один или более потоков, представляемых блоками потоков исполнительной системы (ETH-READ) (см. раздел «Внутреннее устройство потоков» далее в этой главе). Блок EPROCESS и связанные с ним структуры данных — за исключением блока переменных окружения процесса Oprocess environment block, PEB) — существуют в системном пространстве. PEB находится в адресном пространстве процесса, так как содержит данные, модифицируемые кодом пользовательского режима.
Для каждого процесса, выполняющего Windows-программу, процесс подсистемы Windows (Csrss) поддерживает в дополнение к блоку EPROCESS параллельную структуру данных. Кроме того, часть подсистемы Windows, работающая в режиме ядра (Win32k.sys), поддерживает структуру данных для каждого процесса, которая создается при первом вызове потоком любой функции USER или GDI, реализованной в режиме ядра.
Ha рис. 6–1 показана упрощенная схема структур данных процесса и потока. Каждая из этих структур детально рассматривается далее в этой главе.
Рис. 6–1. Структуры данных, сопоставляемые с процессами и потоками
Сначала рассмотрим блок процесса. (Изучение блока потока мы отложим до раздела «Внутреннее устройство потоков».) Ключевые поля EPROCESS показаны на рис. 6–2.
ЭКСПЕРИМЕНТ: исследуем формат блока EPROCESS
Список полей, составляющих блок EPROCESS, и их смещения в шестнадцатеричной форме, можно увидеть с помощью команды dt eprocess отладчика ядра (подробнее об отладчике ядра см. главу 1). Вот что дает эта команда (вывод обрезан для экономии места):
Заметьте, что первое поле (Pcb) на самом деле является подструктурой — блоком процесса, принадлежащим ядру (KPROCESS). Именно здесь хранится информация, используемая при планировании. Для вывода формата блока процесса KPROCESS введите dt_kprocess:
Другой способ просмотра KPROCESS (и прочих подструктур в EPROCESS) — использовать ключ рекурсии (-r) в команде dt. Например, введя dt _eprocess — r1, вы увидите все подструктуры с глубиной вложения, равной 1.
Команда dt показывает формат блока процесса, но не его содержимое. Чтобы вывести экземпляр самого процесса, можно указать адрес структуры EPROCESS в качестве аргумента команды dt. Команда !process 0 0 позволяет получить адрес всех блоков EPROCESS в системе. Пример вывода этой команды будет приведен далее в этой главе.
Некоторые поля, показанные в предыдущем эксперименте, поясняются в таблице 6–1. Процессы и потоки — неотъемлемая часть Windows, о которой нельзя рассказать, не упомянув множество других компонентов системы. Ho, чтобы эта глава не слишком разбухла, мы поясняем механизмы, связанные с процессами и потоками (вроде управления памятью, защиты, объектов и описателей), в других главах.
Таблица 6–1. Содержимое блока EPROCESS
Блок KPROCESS, входящий в блок EPROCESS, и РЕВ, на который указывает EPROCESS, содержат дополнительные сведения об объекте «процесс». Блок KPROCESS, иногда называемый блоком управления процессом Oprocess control block, PCB), показан на рис. 6–3. Он содержит базовую информацию, нужную ядру Windows для планирования потоков. (O каталогах страниц см. главу 7.)
РЕВ, размещаемый в адресном пространстве пользовательского процесса, содержит информацию, необходимую загрузчику образов, диспетчеру кучи и другим системным DLL-модулям Windows для доступа из пользовательского режима. (Блоки EPROCESS и KPROCESS доступны только из режима ядра.) Базовая структура РЕВ, показанная на рис. 6–4, подробнее объясняется далее в этой главе.
ЭКСПЕРИМЕНТ: исследуем PEB
Дамп структуры PEB можно получить с помощью команды !peb отладчика ядра. Чтобы узнать адрес РЕВ, используйте команду !process так:
Переменные ядра
B таблице 6–2 перечислено несколько важнейших глобальных переменных ядра, связанных с процессами. Ha эти переменные мы будем ссылаться по ходу изложения материала, в частности при описании этапов создания процесса.
Таблица 6–2. Переменные ядра, связанные с процессами
Счетчики производительности
Windows поддерживает несколько счетчиков, которые позволяют отслеживать процессы, выполняемые в системе; данные этих счетчиков можно считывать программно или просматривать с помощью оснастки Performance. B таблице 6–3 перечислены счетчики производительности, имеющие отношение к процессам (кроме счетчиков, связанных с управлением памятью и вводом-выводом, которые описываются в главах 7 и 9 соответственно!