Если в течение указанного времени поток не завершается, Csrss открывает диалоговое окно, показанное на рис. 5-5. (Вывод этого окна можно отключить, присвоив параметру HKCU\Control Panel\Desktop\AutoEndTasks значение, равное 1.) Диалоговое окно уведомляет пользователя о том, что корректное завершение данной программы невозможно, и предлагает принудительно завершить процесс или отменить завершение работы системы (тайм-аут для этого диалогового окна не предусмотрен, а значит, на этом этапе запрос на завершение может ждать бесконечно долго).
    Рис. 5-5. Диалоговое окно для принудительного закрытия программы
   Если поток успевает завершиться до истечения указанного времени, Csrss посылает пары сообщений WM_QUERYENDSESSION, WM_ENDSESSION другим потокам процесса с окнами верхнего уровня. Как только все его потоки завершаются, Csrss завершает выполнение этого процесса и переходит к следующему процессу в интерактивном сеансе.
 
    ЭКСПЕРИМЕНТ: проверка HungAppTimeout
   Вы можете проверить, как используется параметр реестра HungAppTimeout, запустив Notepad, введя в него какой-нибудь текст и выйдя из системы. По истечении времени, заданного в HungAppTimeout, Csrss.exe откроет диалоговое окно с запросом о том, хотите ли вы закрыть процесс Notepad, который еще не завершился. Notepad ждет, когда вы сообщите ему, надо ли сохранить введенный вами текст. Если вы нажмете Cancel в этом диалоговом окне, Csrss.exe отменит завершение работы системы.
   Обнаружив консольное приложение, Csrss вызывает обработчик консоли, посылая событие CTRLLOGOFFEVENT (при завершении работы системы только процессы сервисов получают события CTRL_SHUTDOWN_ EVENT). Если обработчик возвращает FALSE, Csrss уничтожает процесс. Если обработчик возвращает TRUE или не отвечает в течение времени, указанного в HKCU\Control Panel\Desktop\WaitToKillAppTimeout (по умолчанию – 20000 мс), Csrss выводит диалоговое окно, показанное на рис. 5-5.
   Далее Winlogon вызывает функцию ExitWindowsEx,чтобы Csrss завершил любые СОМ-процессы, являющиеся частью сеанса интерактивного пользователя.
   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 и базовые системные механизмы, обеспечивающие запуск, работу и в конечном счете выключение системы. Заложив такой фундамент, можно переходить к более подробному изучению отдельных компонентов исполнительной системы, начиная с процессов и потоков.
 

Г Л A B A 6 Процессы, потоки и задания

 
   B этой главе мы рассмотрим структуры данных и алгоритмы, связанные с процессами, потоками и заданиями в Microsoft Windows. B первом разделе основное внимание уделяется внутренним структурам данных, из которых состоит процесс. Bo втором разделе поясняются этапы создания процесса (и его первичного потока). Далее поясняются внутреннее устройство потоков и их планирование. Завершается глава описанием объекта «задание» Jobobject).
   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 соответственно!
 
Сопутствующие функции
   B таблице 6-4 приведена информация по некоторым Windows-функциям, связанным с процессами. Более подробные сведения см. в документации Windows API в MSDN Library.
 
    ЭКСПЕРИМЕНТ: применение команды !process отладчика ядра
   Эта команда выводит подмножество информации из блока EPROCESS. Ee вывод для каждого процесса делится на две части. Сначала вы видите часть, показанную ниже (если вы не указываете адрес или идентификатор процесса, команда !processвыводит сведения для активного процесса на текущем процессоре).
   Вслед за базовой информацией о процессе появляется список его потоков. Данная часть поясняется в эксперименте «Применение команды !threadотладчика ядра» далее в этой главе. Еще одна команда, позволяющая получить информацию о процессе, – !handle.Она создает дамп таблицы описателей, принадлежащей процессу (см. раздел «Описатели объектов и таблица описателей, принадлежащая процессу» главы 3). Структуры защиты процессов и потоков описываются в главе 8.
 
Что делает функция CreateProcess
   K этому моменту мы уже рассмотрели структуры, которые являются частью процесса, и API-функции, позволяющие вам (и операционной системе) манипулировать процессами. Вы также научились пользоваться различными утилитами для наблюдения за тем, как процессы взаимодействуют с системой. Ho как эти процессы появляются на свет и как они завершаются, выполнив задачи, для которых они предназначались? B следующих разделах вы узнаете, как порождаются Windows-процессы.
   Создание Windows-процесса осуществляется вызовом одной из таких функций, как CreateProcess, CreateProcessAsUser, CreateProcessWithTokenWили CreateProcessWitbLogonW,и проходит в несколько этапов с участием трех компонентов операционной системы: Kernel32.dll (библиотеки клиентской части Windows), исполнительной системы и процесса подсистемы окружения Windows (Csrss). Поскольку архитектура Windows поддерживает несколько подсистем окружения, операции, необходимые для создания объекта «процесс» исполнительной системы (которым могут пользоваться и другие подсистемы окружения), отделены от операций, требуемых для создания Windows-процесса. Поэтому часть действий Windows-функции CreateProcessспецифична для семантики, привносимой подсистемой Windows.
   B приведенном ниже списке перечислены основные этапы создания процесса Windows-функцией CreateProcess.Детальное описание действий на каждом этапе дается в следующих разделах.
 
    ПРИМЕЧАНИЕ Многие этапы работы CreateProcess связаны с подготовкой виртуального адресного пространства процесса и поэтому требуют понимания массы структур и терминов, связанных с управлением памятью и описываемых в главе 7.
 
   1. Открывается файл образа (EXE), который будет выполняться в процессе.
   2. Создается объект «процесс» исполнительной системы.
   3. Создается первичный поток (стек, контекст и объект «поток» исполнительной системы).
   4. Подсистема Windows уведомляется о создании нового процесса и потока.
   5. Начинается выполнение первичного потока (если не указан флаг CREATE_SUSPENDED).
   6. B контексте нового процесса и потока инициализируется адресное пространство (например, загружаются требуемые DLL) и начинается выполнение программы.
 
   Общая схема создания процесса в Windows показана на рис. 6-5. Прежде чем открыть исполняемый образ для выполнения, CreateProcess делает следующее.
    (o)При вызове CreateProcessкласс приоритета указывается в параметре CreationFlags,и, вызывая CreateProcess,вы можете задать сразу несколько классов приоритета. Windows выбирает самый низкий из них.
    (o)Когда для нового процесса не указывается класс приоритета, по умолчанию принимается Normal, если только класс приоритета процесса-создателя не равен IdIe или Below Normal. B последнем случае новый процесс получает тот же класс приоритета, что и у родительского процесса.
    (o)Если для нового процесса указан класс приоритета Real-time, а создатель не имеет привилегии Increase Scheduling Priority, устанавливается класс приоритета High. Иначе говоря, функция CreateProcessзавершается успешно, даже если у того, кто ее вызвал, недостаточно привилегий для создания процессов с классом приоритета Real-time, – просто класс приоритета нового процесса будет ниже Real-time.
    (o)Все окна сопоставляются с объектами «рабочий стол», которые являются графическим представлением рабочего пространства. Если при вызове CreateProcessне указан конкретный объект «рабочий стол», новый процесс сопоставляется с текущим объектом «рабочий стол» процесса-создателя.
 
Этап 1: открытие образа, подлежащего выполнению
   Как показано на рис. 6-6 и в таблице 6-5, на первом этапе CreateProcessдолжна найти нужный Windows-образ, который будет выполнять файл, указанный вызвавшим процессом, и создать объект «раздел» для его последующего проецирования на адресное пространство нового процесса. Если имя образа не указано, используется первая лексема командной строки (первая часть командной строки, которая заканчивается пробелом или знаком табуляции и является допустимой в качестве имени образа).
   B Windows XP и Windows Server 2003 CreateProcessпроверяет, не запрещает ли политика безопасности на данной машине запуск этого образа (см. главу 8).
   Если в качестве исполняемого файла указана Windows-программа, ее имя используется напрямую. A если исполняемый файл является не Windows-приложением, а программой MS-DOS, Winl6 или POSIX, то CreateProcessищет образ поддержки (support image) для запуска этой программы. Данный процесс необходим потому, что приложения, не являющиеся Windows-программами, нельзя запускать напрямую. Вместо этого Windows использует один из нескольких специальных образов поддержки, которые и отвечают за запуск приложений, отличных от Windows-программ. Так, если вы пытаетесь запустить POSIX-приложение, CreateProcessидентифицирует его как таковое и вызывает исполняемый Windows-файл поддержки POSIX, Posix.exe. A если вы запускаете программу MS-DOS или Winl6, стартует исполняемый Windows-файл поддержки Ntvdm.exe. Короче говоря, вы не можете напрямую создать процесс, неявляющийся Windows-процессом. Если Windows не найдет соответствующий файл поддержки, вызов CreateProcessзакончится неудачей.
   Конкретное решение о запуске того или иного файла поддержки CreateProcessпринимает так.
    (o)Если исполняемый файл – программа MS-DOS с расширением EXE, COM или PIF, подсистеме Windows посылается сообщение, чтобы она проверила, не создан ли уже процесс поддержки MS-DOS (Ntvdm.exe, указанный в параметре реестра HKLM\SYSTEM\CurrentControlSet\Control\WOW\ Cmdline). Если да, этот процесс используется для запуска программы MS-DOS – подсистема Windows посылает виртуальной DOS-машине (Virtual DOS Machine, VDM) сообщение для запуска новой программы, – после чего управление возвращается к CreateProcess.Если нет, запускается Ntvdm.exe и повторно выполняется первый этап CreateProcess.
    (o)Если исполняемый файл – командный файл с расширением BAT или CMD, запускается Cmd.exe, обрабатывающий командную строку Windows, и повторно выполняется первый этап CreateProcess.(Имя командного файла передается Cmd.exe как первый параметр.)
    (o)Если исполняемый файл – приложение Winl6 (Windows 3.1), CreateProcessрешает, надо ли для его запуска создавать новый процесс VDM или оно должно использовать глобальный для всех сеансов процесс VDM (который, возможно, еще не создан). Решение определяется флагами CREATE_SEPARATE_WOW_VDM и CREATE_SHARED_WOW_VDM. Если эти флаги не заданы, то по умолчанию решение принимается, исходя из значения параметра реестра HKLM\SYSTEM\CurrentControlSet\Control\WOW\DefaultSeparateVDM. Если программа будет работать в отдельной VDM, запускается приложение, указанное в HKLM\SYSTEM\CurrentControlSet\Control\WOW\WowCmdline, и повторно выполняется первый этап CreateProcess.B ином случае подсистема Windows посылает сообщение для проверки возможности использования общего процесса VDM. (Это исключено, если процесс VDM сопоставлен с другим объектом «рабочий стол» или если его параметры защиты отличны от таковых для вызывающего процесса. Тогда нужно создавать новый процесс VDM.) Если задействовать общий процесс VDM нельзя, подсистема Windows посылает ему сообщение о необходимости запуска нового образа, и управление возвращается к CreateProcess.Если процесс VDM еще не создан (или если он существует, но использовать его нельзя), запускается образ поддержки VDM и повторно выполняется первый этап CreateProcess.K этому моменту CreateProcessуспешно открывает допустимый исполняемый файл Windows и создает для него объект «раздел». Этот объект еще не спроецирован на память, но уже открыт. Однако сам факт успешного создания объекта «раздел» не означает того, что запускаемый файл является допустимым Windows-образом, – он может быть DLL или исполняемым файлом POSIX. Если это исполняемый файл POSIX, запускается Posix.exe, и CreateProcessзаново выполняет действия первого этапа. A если это DLL, вызов CreateProcessзаканчивается неудачей.
    CreateProcess,найдя допустимый исполняемый Windows-образ, ищет в разделе реестра HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options подраздел с именем и расширением запускаемого образа (но без указания пути к нему, например Image.exe). Если такой подраздел есть, CreateProcessищет в нем параметр Debugger. Если он присутствует, его значение становится именем запускаемого образа, после чего следует повторение первого этапа CreateProcess.
 
    СОВЕТ Вы можете извлечь выгоду из такого поведения CreateProcess. Оно позволяет отлаживать стартовый код процессов сервисов Windows перед их запуском. Если бы вы подключили отладчик лишь после запуска сервиса, это исключило бы возможность отладки стартового кода.
 
Этап 2: создание объекта «процесс»
   K началу второго этапа функция CreateProcessуже открыла допустимый исполняемый файл Windows и создала объект «раздел» для его проецирования на адресное пространство нового процесса. После этого она создает объект «процесс», чтобы запустить образ вызовом внутренней функции NtCreateProcess.Создание объекта «процесс» исполнительной системы включает следующие подэтапы:
    (o)формируется блок EPROCESS;
    (o)создается начальное адресное пространство процесса; (o)инициализируется блок процесса ядра (KPROCESS);
    (o)инициализируется адресное пространство процесса (в том числе список рабочего набора и дескрипторы виртуального адресного пространства), а также проецируется образ на это пространство;
    (o)формируется блок РЕВ;
    (o)завершается инициализация объекта «процесс» исполнительной системы.
 
    ПРИМЕЧАНИЕ Родительские процессы отсутствуют только при инициализации системы. Далее они всегда используются для задания контекстов защиты новых процессов.
 
Этап 2A: формирование блока EPROCESS
   Этот подэтап включает девять операций.
   1. Создается и инициализируется блок EPROCESS.
   2. От родительского процесса наследуется маска привязки к процессорам.
   3. Минимальный и максимальный размеры рабочего набора процесса устанавливаются равными значениям переменных PsMinimumWorkingSetи PsMaximumWorkingSet.
   4. Блок квот нового процесса настраивается на адрес блока квот его родительского процесса и увеличивается счетчик ссылок на блок квот последнего.
   5. Наследуется пространство имен устройств Windows (в том числе определение букв дисков, СОМ-портов и т. д.).
   6. B поле InheritedFromUniqueProcessIdнового объекта «процесс» сохраняется идентификатор родительского процесса.
   7. Создается основной маркер доступа процесса (копированием аналогичного маркера родительского процесса). Новый процесс наследует профиль защиты своих родителей. Если используется функция CreateProcessAsUser,чтобы задать для нового процесса другой маркер доступа, он соответственно модифицируется.
   8. Инициализируется таблица описателей, принадлежащая процессу. Если установлен флаг наследования описателей родительского процесса, наследуемые описатели из его таблицы копируются в новый процесс (о таблицах описателей см. главу 3).
   9. Статус завершения нового процесса устанавливается как STATUSPENDING.
 
Этап 2B: создание начального адресного пространства процесса
   Начальное адресное пространство процесса состоит из следующих страниц:
    (o)каталога страниц (этих каталогов может быть больше одного в системах, где таблицы страниц имеют более двух уровней, например в х8б-систе-мах в режиме PAE или в б4-разрядных системах);
    (o)страницы гиперпространства;
    (o)списка рабочего набора.
 
   Для создания этих страниц выполняются операции, перечисленные ниже.
   1. B соответствующих таблицах страниц формируются записи, позволяющие проецировать эти начальные страницы. Количество страниц вычитается из переменной ядра MmTotalCommittedPagesи добавляется к переменной ядра MmProcessCommit.
   2. Из MmResidentAvailablePagesвычитается минимальный размер рабочего набора по умолчанию (PsMinimumWorkingSef).
   3. Ha адресное пространство процесса проецируются страницы таблицы страниц для неподкачиваемой части системного пространства и системного кэша.
 
Этап 2C: создание блока процесса ядра
   Ha этом подэтапе работы CreateProcessинициализируется блок KPROCESS, содержащий указатель на список потоков ядра. (Ядро не имеет представления об описателях, поэтому оно обходит их таблицу.) Блок процесса ядра также указывает на каталог таблицы страниц процесса (используемый для отслеживания виртуального адресного пространства процесса) и содержит суммарное время выполнения потоков процесса, базовый приоритет процесса по умолчанию (он начинается с Normal, или 8, если только его значение у родительского процесса не равно IdIe или Below Normal; в последнем случае приоритет просто наследуется), привязку потоков к процессорам по умолчанию и начальный квант процессорного времени, выделяемый процессу по умолчанию. Последнее значение принимается равным PspForegroundQuantum[0],первому элементу общесистемной таблицы величин квантов.