Теперь, изучив анатомию процессов, рассмотрим структуру потоков. Там, где явно не сказано обратное, считайте, что весь материал этого раздела в равной мере относится как к обычным потокам пользовательского режима, так и к системным потокам режима ядра (описанным в главе 3).
Ha уровне операционной системы поток представляется блоком потока, принадлежащим исполнительной системе (ETHREAD). Структура этого блока показана на рис. 6–7. Блок ETHREAD и все структуры данных, на которые он ссылается, существуют в системном адресном пространстве, кроме блока переменных окружения потока (thread environment block, TEB) — он размещается в адресном пространстве процесса. Помимо этого, процесс подсистемы Windows (Csrss) поддерживает параллельную структуру для каждого потока, созданного в Windows-процессе. Часть подсистемы Windows, работающая в режиме ядра (Win32k.sys), также поддерживает для каждого потока, вызывавшего USER- или GDI-функцию, структуру W32THREAD, на которую указывает блок ETHREAD.
Поля блока потока, показанные на рис. 6–7, в большинстве своем не требуют дополнительных пояснений. Первое поле — это блок потока ядра (KTHREAD). За ним следуют идентификационные данные потока и процесса (включая указатель на процесс — владелец данного потока, что обеспечивает доступ к информации о его окружении), затем информация о защите в виде указателя на маркер доступа и сведения, необходимые для олицетворения (подмены одного процесса другим), а также поля, связанные с сообщениями LPC и незавершенными запросами на ввод-вывод. B таблице 6–8 даны ссылки на другие части книги, где некоторые из наиболее важных полей описываются подробнее. Чтобы получить более детальные сведения о внутренней структуре блока ETHREAD, используйте команду dt отладчика ядра.
Давайте повнимательнее присмотримся к двум ключевым структурам потока, упомянутым выше, — к блокам KTHREAD и TEB. Первый содержит информацию, нужную ядру Windows для планирования потоков и их синхронизации с другими потоками. Схема блока KTHREAD показана на рис. 6–8.
Ключевые поля блока KTHREAD кратко рассмотрены в таблице 6–9.
Таблица 6–9. Ключевые поля блока KTHREAD
ЭКСПЕРИМЕНТ: просмотр структур ETHREAD и KTHREAD
Структуры ETHREAD и KTHREAD можно просмотреть с помощью команды dt отладчика ядра. B следующем выводе показан формат ETHREAD:
Для просмотра KTHREAD предназначена аналогичная команда:
ЭКСПЕРИМЕНТ: использование команды !thread отладчика ядра
Команда !thread отладчика ядра выдает дамп подмножества информации из структур данных потока. Отладчик ядра выводит ряд важных данных, не показываемых любыми другими утилитами: адреса внутренних структур, детальные сведения о приоритетах, данные стека, список незавершенных запросов на ввод-вывод и список ожидаемых объектов для тех потоков, которые находятся в состоянии ожидания.
Чтобы получить информацию о потоке, используйте либо команду !process (которая выводит все блоки потоков после блока процесса), либо команду !thread (которая сообщает сведения только об указанном потоке). Ниже дан пример информации о потоке с пояснением ее важнейших полей.
ЭКСПЕРИМЕНТ: просмотр информации о потоке
Утилита Tlist из Windows Debugging Tools позволяет получить подробную информацию о процессе, пример которой приведен ниже. Заметьте, что в списке потоков указывается «Win32StartAddress». Это адрес, передаваемый функции CreateThread приложением. Остальные утилиты, кроме Process Explorer, показывающие стартовый адрес потока, выводят его истинный стартовый адрес, а не стартовый адрес, заданный приложением.
B отличие от других структур данных, описываемых в этом разделе, только блок TEB, показанный на рис. 6–9, присутствует в адресном пространстве процесса, а не системы. B TEB хранится контекстная информация загрузчика образов и различных Windows DLL. Поскольку эти компоненты выполняются в пользовательском режиме, им нужны структуры данных, доступные для записи из этого режима. Вот почему TEB размещается в адресном пространстве процесса, а не системы, где он был бы доступен для записи только из режима ядра. Адрес TEB можно найти с помощью команды !thread отладчика ядра.
ЭКСПЕРИМЕНТ: исследуем TEB
Вы можете получить дамп структуры TEB, используя команду !teb отладчика ядра. Ee вывод выглядит так:
Как и в случае процессов, ряд переменных ядра Windows контролирует выполнение потоков. Список таких переменных, связанных с потоками, приводится в таблице 6-10.
Таблица 6-10. Переменные ядра, относящиеся к потокам
Большая часть важной информации в структурах данных потоков экспортируется в виде счетчиков производительности, перечисленных в таблице 6-11. Даже используя только оснастку Performance, вы можете получить довольно много сведений о внутреннем устройстве потоков.
B таблице 6-12 перечислены Windows-функции, позволяющие создавать потоки и манипулировать ими. Здесь не показаны функции, связанные с планированием и управлением приоритетами потоков, — они описываются в разделе «Планирование потоков» далее в этой главе.
Жизненный цикл потока начинается при его создании программой. Запрос на его создание в конечном счете поступает исполнительной системе Windows, где диспетчер процессов выделяет память для объекта «поток» и вызывает ядро для инициализации блока потока ядра. Ниже перечислены основные этапы создания потока Windows-функцией CreateThread (которая находится в Kernel32.dll).
1. CreateThread создает стек пользовательского режима в адресном пространстве процесса.
2. CreateThread инициализирует аппаратный контекст потока, специфичный для конкретной архитектуры процессора. (Подробнее о блоке контекста потока см. раздел справочной документации Windows API по структуре CONTEXT.)
3. Для создания объекта «поток» исполнительной системы вызывается Nt-CreateThread. Он создается в приостановленном состоянии. Описание операций, выполняемых NtCreateThread, см. в разделе «Что делает функция CreateProcess» (этапы 3 и 6) ранее в этой главе.