Марк Руссинович - 2.Внутреннее устройство Windows (гл. 5-7)
• Transition (переходное состояние) Поток переходит в это состояние, если он готов к выполнению, но его стек ядра выгружен из памяти. Как только этот стек загружается в память, поток переходит в состояние Ready.
• Terminated (завершен) Заканчивая выполнение, поток переходит в состояние Terminated. После этого блок потока исполнительной системы (структура данных в пуле неподкачиваемой памяти, описывающая данный поток) может быть удален, а может быть и не удален — это уже определяется диспетчером объектов.
• 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. Заметьте, что один из потоков процесса 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-битную маску, называемую сводкой готовности (ready summary) (KiReadySummary). Каждый установленный в ней бит указывает на присутствие одного или более потоков в очереди готовых потоков для данного уровня приоритета (бит 0 соответствует приоритету 0, бит 1 — приоритету 1 и т. д.).
B таблице 6-15 перечислены переменные ядра, связанные с планированием потоков в однопроцессорных системах.
B однопроцессорных системах база данных диспетчера ядра синхронизируется повышением IRQL до уровня «DPC/dispatch» и SYNCH_LEVEL (оба определены как уровень 2). (Об уровнях приоритета прерываний см. раздел «Диспетчеризация ловушек» главы 3) Такое повышение IRQL не дает другим потокам прервать диспетчеризацию потоков, так как потоки обычно выполняются при IRQL O или 1. B многопроцессорных системах одного повышения IRQL мало, потому что каждый процессор может одновременно увеличить IRQL до одного уровня и попытаться обратиться к базе данных диспетчера ядра. Как Windows синхронизирует доступ к этой базе данных в многопроцессорных системах, поясняется в разделе «Многопроцессорные системы» далее в этой главе.
КвантКак уже говорилось, квант — это интервал процессорного времени, отведенный потоку для выполнения. По его окончании Windows проверяет, ожидает ли выполнения другой поток с таким же уровнем приоритета. Если на момент истечения кванта других потоков с тем же уровнем приоритета нет, Windows выделяет текущему потоку еще один квант.
По умолчанию в Windows 2000 Professional и Windows XP потоки выполняются в течение 2 интервалов таймера (clock intervals), а в системах Windows Server — 12. (Как изменить эти значения, мы объясним позже.) B серверных системах величина кванта увеличена для того, чтобы свести к минимуму переключение контекста. Получая больший квант, серверные приложения, которые пробуждаются при получении клиентского запроса, имеют больше шансов выполнить запрос и вернуться в состояние ожидания до истечения выделенного кванта.
Длительность интервала таймера зависит от аппаратной платформы и определяется HAL, а не ядром. Например, этот интервал на большинстве однопроцессорных х86-систем составляет 10 мс, а на большинстве многопроцессорных х86-систем — около 15 мс. (Как проверить реальный интервал системного таймера, см. в следующем эксперименте.)
ЭКСПЕРИМЕНТ: определяем величину интервала системного таймера
Windows-функция GetSystemTimeAdjustment возвращает величину интервала системного таймера. Для ее определения запустите программу Clockres с sysinternals.com. Вот что это программа выводит на однопроцессорной х86-системе:
C: ›clockres
ClockRes — View the system clock resolution
By Mark Russinovich
SysInternals — www.sysinternals.com
The system clock interval is 10.014400 ms
Учет квантов времениВеличина кванта для каждого процесса хранится в блоке процесса ядра. Это значение используется, когда потоку предоставляется новый квант. Когда поток выполняется, его квант уменьшается по истечении каждого интервала таймера, и в конечном счете срабатывает алгоритм обработки завершения кванта. Если имеется другой поток с тем же приоритетом, ожидающий выполнения, происходит переключение контекста на следующий поток в очереди готовых потоков. Заметьте: когда системный таймер прерывает DPC или процедуру обработки другого прерывания, квант выполнявшегося потока все равно уменьшается, даже если этот поток не успел отработать полный интервал таймера. Если бы это было не так и если бы аппаратное прерывание или DPC появилось непосредственно перед прерыванием таймера, квант потока мог бы вообще никогда не уменьшиться.
Внутренне величина кванта хранится как число тактов таймера, умноженное на 3. To есть в Windows 2000 и Windows XP потоки по умолчанию получают квант величиной 6 (2 * 3), в Windows Server — 36 (12 * 3). Всякий раз, когда возникает прерывание таймера, процедура его обработки вычитает из кванта потока постоянную величину (3).
Почему квант внутренне хранится как величина, кратная трем квантовым единицам за один такт системного таймера? Это сделано для того, чтобы можно было уменьшать значение кванта по завершении ожидания. Когда поток с текущим приоритетом ниже 16 и базовым приоритетом ниже 14 запускает функцию ожидания (WaitForSingleObject или WaitForMultipleObjects) и его запрос на доступ удовлетворяется немедленно (например, он не переходит в состояние ожидания), его квант уменьшается на одну единицу. Благодаря этому кванты ожидающих потоков в конечном счете заканчиваются.