Всегда ли данный алгоритм решает проблемы, связанные с приоритетами? Вовсе нет — он тоже не совершенен. Ho со временем потоки, страдающие от нехватки процессорного времени, обязательно получают время, достаточное для завершения обработки текущих данных и возврата в состояние ожидания.
ЭКСПЕРИМЕНТ: динамическое повышение приоритетов при нехватке процессорного времени
Утилита CPU Stress (она входит в состав ресурсов и Platform SDK) позволяет наблюдать, как работает механизм динамического повышения приоритетов. B этом эксперименте мы увидим, как изменяется интенсивность использования процессора при повышении приоритета потока. Для этого проделайте следующие операции. 1. Запустите Cpustres.exe. Измените значение в списке Activity для активного потока (по умолчанию — Thread 1) с Low на Maximum. Далее смените приоритет потока с Normal на Below Normal. При этом окно утилиты должно выглядеть так:
2. Запустите Windows NT 4 Performance Monitor (Perfmon4.exe на компакт-диске ресурсов Windows 2000). Нам снова понадобится эта устаревшая версия, поскольку она запрашивает значения счетчиков чаще, чем раз в секунду.
3. Щелкните на панели инструментов кнопку Add Counter (или нажмите клавиши Ctrl+I), чтобы открыть диалоговое окно Add To Chart.
4. Выберите объект Thread и счетчик % Processor Time.
5. Пролистайте список Instance и найдите процесс Cpustres. Выберите второй поток под номером 1, так как первый (под номером 0) является потоком GUI.
6. Щелкните кнопку Add, затем — кнопку Done.
7. Увеличьте приоритет Performance Monitor до уровня реального времени. Для этого запустите Task Manager (Диспетчер задач) и выберите процесс Perfmon4.exe на вкладке Processes (Процессы). Щелкните имя процесса правой кнопкой мыши, выберите Set Priority (Приоритет) и укажите Realtime (Реального времени). При этом вы получите предупреждение о возможности нестабильной работы системы — щелкните кнопку Yes (Да).
8. Запустите еще один экземпляр CPU Stress. Измените в нем параметр Activity для Thread 1 с Low на Maximum.
9. Теперь переключитесь обратно в Performance Monitor. Вы должны наблюдать всплески активности процессора примерно каждые 4 секунды, так как приоритет потока возрос до 15.
Закончив эксперимент, закройте Performance Monitor и все экземпляры CPU Stress.
ЭКСПЕРИМЕНТ: «прослушивание» динамического повышения приоритета
Чтобы «услышать» эффект динамического повышения приоритета потока при нехватке процессорного времени, в системе со звуковой платой выполните следующие операции.
1. Запустите Windows Media Player (или другую программу для воспроизведения музыки) и откройте какой-нибудь музыкальный файл.
2. Запустите Cpustres из ресурсов Windows 2000 и задайте для потока 1 максимальный уровень активности.
3. Повысьте приоритет потока 1 с Normal до Time Critical.
4. Воспроизведение музыки должно остановиться, так как вычисления, выполняемые потоком, расходуют все процессорное время.
5. Время от времени вы должны слышать отрывочные звуки музыки, когда приоритет «голодающего» потока в процессе, который воспроизводит музыкальный файл, динамически повышается до 15 и он успевает послать на звуковую плату порцию данных.
6. Закройте Cpustres и Windows Media Player.
Многопроцессорные системы
B однопроцессорной системе алгоритм планирования относительно прост: всегда выполняется поток с наивысшим приоритетом, готовый к выполнению. B многопроцессорной системе планирование усложняется, так как Windows пытается подключать поток к наиболее оптимальному для него процессору, учитывая предпочтительный и предыдущий процессоры для этого потока, а также конфигурацию многопроцессорной системы. Поэтому, хотя Windows пытается подключать готовые к выполнению потоки с наивысшим приоритетом ко всем доступным процессорам, она гарантирует лишь то, что на одном из процессоров будет работать (единственный) поток с наивысшим приоритетом.
Прежде чем описывать специфические алгоритмы, позволяющие выбирать, какие потоки, когда и на каком процессоре будут выполняться, давайте рассмотрим дополнительную информацию, используемую Windows для отслеживания состояния потоков и процессоров как в обычных многопроцессорных системах, так и в двух новых типах таких систем, поддерживаемых Windows, — в системах с физическими процессорами, поддерживающими логические (hyperthreaded systems), и NUMA.
База данных диспетчера ядра в многопроцессорной системе
Как уже говорилось в разделе «База данных диспетчера ядра» ранее в этой главе, в такой базе данных хранится информация, поддерживаемая ядром и необходимая для планирования потоков. B многопроцессорных системах Windows 2000 и Windows XP (рис. 6-15) очереди готовых потоков и сводка готовых потоков имеют ту же структуру, что и в однопроцессорных системах. Кроме того, Windows поддерживает две битовые маски для отслеживания состояния процессоров в системе. (Как используются эти маски, см. в разделе «Алгоритмы планирования потоков в многопроцессорных системах» далее в этой главе.) Вот что представляют собой эти маски.
• Маска активных процессоров (KeActiveProcessors), в которой устанавливаются биты для каждого используемого в системе процессора. (Их может быть меньше числа установленных процессоров, если лицензионные ограничения данной версии Windows не позволяют задействовать все физические процессоры.)
• Сводка простоя (idle summary) (KiIdleSummary), в которой каждый установленный бит представляет простаивающий процессор. Если в однопроцессорной системе диспетчерская база данных блокируется повышением IRQL (в Windows 2000 и Windows XP до уровня «DPC/ dispatch», а в Windows Server 2003 до уровней «DPC/dispatch» и «Synch»), то в многопроцессорной системе требуется большее, потому что каждый процессор одновременно может повысить IRQL и попытаться манипулировать этой базой данных. (Кстати, это относится к любой общесистемной структуре, доступной при высоких IRQL. Общее описание синхронизации режима ядра и спин-блокировок см. в главе 3.) B Windows 2000 и Windows XP для синхронизации доступа к информации о диспетчеризации потока применяется две спин-блокировки режима ядра: спин-блокировка диспетчера ядра (dispatcher spinlock) (KiDispatcherLock) и спин-блокировка обмена контекста (context swap spinlock) (KiContextSwapLocM). Первая удерживается, пока вносятся изменения в структуры, способные повлиять на то, как должен выполняться поток, а вторая захватывается после принятия решения, но в ходе самой операции обмена контекста потока.
Для большей масштабируемости и улучшения поддержки параллельной диспетчеризации потоков в многопроцессорных системах Windows Server 2003 очереди готовых потоков диспетчера создаются для каждого процессора, как показано на рис. 6-23. Благодаря этому в Windows Server 2003 каждый процессор может проверять свои очереди готовых потоков, не блокируя аналогичные общесистемные очереди.
Очереди готовых потоков и сводки готовности, индивидуальные для каждого процессора, являются частью структуры PRCB (processor control block). (Чтобы увидеть поля этой структуры, введите dt nt!_prcb в отладчике ядра.) Поскольку в многопроцессорной системе одному из процессоров может понадобиться изменить структуры данных, связанные с планированием, для другого процессора (например, вставить поток, предпочитающий работать на определенном процессоре), доступ к этим структурам синхронизируется с применением новой спин-блокировки с очередями, индивидуальной для каждой PRCB; она захватывается при IRQL SYNCH_LEVEL. (Возможные значения SYNCH_LEVEL см. в таблице 6-18.) Таким образом, поток может быть выбран при блокировке PRCB лишь какого-то одного процессора — в отличие от Windows 2000 и Windows XP, где с этой целью нужно захватить общесистемную спин-блокировку диспетчера ядра.