Добавлен: 29.10.2018
Просмотров: 48022
Скачиваний: 190
11.3. Структура системы
961
ства выполнения кода по завершении ввода-вывода функция QueueUserAPC в Win32
API позволяет также использовать АРС для произвольных целей.
Исполнительный уровень использует АРС и для других операций (помимо завершения
ввода-вывода). Поскольку механизм АРС тщательно спроектирован для того, чтобы
поставлять АРС только тогда, когда это можно сделать безопасно, то его можно ис-
пользовать для безопасного завершения потоков. Если время для завершения потока
неподходящее, то поток объявляет, что он вошел в критическую область, и откладывает
доставку АРС до момента выхода из нее. Потоки ядра помечают себя в качестве вхо-
дящих в критическую область (для откладывания доставки АРС) перед получением
блокировок или других ресурсов, чтобы их нельзя было завершить во время удержания
ими ресурсов.
Диспетчерские объекты
Еще один тип объектов синхронизации — диспетчерские объекты. Это любой из
обычных объектов режима ядра (на которые пользователи могут ссылаться при по-
мощи описателей), который содержит структуру данных под названием «заголовок
диспетчеризации» (рис. 11.6).
Рис. 11.6. Структура данных dispatcher_header имеется во многих объектах исполнительной
системы (диспетчерских объектах)
Это могут быть семафоры, мьютексы, события, таймеры ожидания и прочие объекты,
которых могут ожидать потоки для синхронизации своего выполнения с другими пото-
ками. Сюда также входят объекты, представляющие открытые файлы, процессы, потоки
и порты IPC. Структура данных диспетчеризации содержит флаг, представляющий сиг-
нальное состояние объекта, а также очередь потоков, ожидающих сигнализации объекта.
Примитивы сигнализации (такие, как семафоры) являются естественными диспет-
черскими объектами. Таймеры, файлы, порты, потоки и процессы также используют
механизм диспетчерских объектов для уведомлений. При срабатывании таймера, за-
вершении ввода-вывода в файл, появлении в порту данных, завершении потока или
процесса соответствующий диспетчерский объект сигнализирует, пробуждая все по-
токи, которые ждут этого события.
Поскольку Windows использует для синхронизации с объектами режима ядра единый
механизм, то специализированные API (такие, как wait3, в UNIX — для ожидания
дочерних процессов) для ожидания событий не нужны. Часто потоки хотят ждать
сразу многих событий. В UNIX процесс может ждать (при помощи системного вызова
select) появления данных в любом из 64 сетевых сокетов. В Windows есть аналогичный
API с названием WaitForMultipleObjects, но он позволяет потоку ждать диспетчерский
объект любого типа (для которого у него есть описатель). Для WaitForMultipleObjects
962
Глава 11. Изучение конкретных примеров: Windows 8
можно указать до 64 описателей, а также необязательное значение тайм-аута. Поток
становится готовым к выполнению тогда, когда сигнализирует любой из связанных
с описателями объектов (или происходит тайм-аут).
Фактически существуют две разные процедуры, которые используются ядром для
перевода в работоспособное состояние потоков, ожидающих диспетчерских. Сигна-
лизирование объекта уведомления (notification object) сделает готовыми к работе
все ожидающие потоки. Объекты синхронизации (synchronization objects) делают
работоспособным только первый ждущий поток и используются для тех диспетчер-
ских объектов, которые реализуют примитивы блокировки (вроде мьютексов). Когда
ждущий блокировки поток опять начинает выполняться, то прежде всего он опять
пытается получить блокировку. Если блокировку может удерживать одновременно
только один поток, то все остальные ставшие работоспособными потоки могут немед-
ленно заблокироваться (что может привести к большому количеству ненужных пере-
ключений контекста). Разница между использующими синхронизацию и уведомление
диспетчерскими объектами состоит во флаге в структуре dispatcher_header.
В качестве небольшого отступления: мьютексы в Windows называют мутантами кода,
поскольку они требовались для реализации семантики OS/2, где они автоматически не
разблокировали сами себя при выходе удерживающего их потока, что Катлер считал
неестественным.
Исполнительный уровень
Как показано на рис. 11.4, ниже уровня ядра NTOS находится исполнительная система.
Этот исполнительный уровень написан на языке С, он в основном не зависим от архи-
тектуры (примечательное исключение — диспетчер памяти) и был с минимальными
усилиями перенесен на новые процессоры (MIPS, x86, PowerPC, Alpha, IA64, x64
и ARM). Исполнительный уровень содержит несколько различных компонентов, все
они работают при помощи абстракций управления, предоставляемых уровнем ядра.
Все компоненты разделены на внутренние и внешние структуры данных и интерфейсы.
Внутренние аспекты всех компонентов скрыты и используются только внутри самого
компонента, в то время как внешние аспекты доступны всем остальным компонентам
исполнительного уровня. Некоторое подмножество внешних интерфейсов экспорти-
руется из ntoskrnl.exe, и драйверы устройств могут привязаться к ним так, как будто
исполнительный уровень является библиотекой. Компания Microsoft называет многие
компоненты исполнительного уровня диспетчерами, поскольку каждый из них отвеча-
ет за управление каким-то аспектом работы (вводом-выводом, памятью, процессами,
объектами и т. д.).
Как и в большинстве операционных систем, большая часть функциональности испол-
нительного уровня Windows подобна библиотечному коду, за исключением того, что
он выполняется в режиме ядра, так что его структуры данных могут использоваться
совместно и защищаться от доступа кода пользовательского режима, поэтому он может
обращаться к состоянию режима ядра (например, к управляющим регистрам блока
управления памятью — MMU). Но в остальном исполнительный уровень просто вы-
полняет функции операционной системы от имени вызвавшей их стороны и таким
образом работает в потоке вызвавшей стороны.
Когда какая-либо из функций исполнительного уровня блокируется в ожидании син-
хронизации с другими потоками, то поток пользовательского режима также блокиру-
11.3. Структура системы
963
ется. Это имеет смысл в том случае, когда работа выполняется от имени конкретного
потока пользовательского режима, но может быть несправедливым при выполнении
такой работы, которая связана с обычными «хозяйственными» задачами. Для того
чтобы предотвратить «кражу» текущего потока в том случае, когда исполнительный
уровень определяет, что нужно выполнить некоторую «хозяйственную» работу, при
загрузке системы создается несколько потоков режима ядра (которые распределяют-
ся конкретным задачам, таким как обеспечение записи на диск модифицированных
страниц).
Для предсказуемых и нечастых задач имеется поток, который срабатывает раз в секун-
ду и имеет список обрабатываемых задач. Для менее предсказуемой работы существует
пул высокоприоритетных рабочих потоков (мы их уже упоминали), которые можно
использовать для выполнения задач путем постановки запросов в очередь и сигнали-
зирования события синхронизации, которого ожидают рабочие потоки.
Диспетчер объектов
(object manager) управляет большинством интересных объектов
режима ядра, используемых на исполнительном уровне. Это процессы, потоки, файлы,
семафоры, устройства и драйверы ввода-вывода, таймеры и многое другое. Как уже
описывалось, объекты режима ядра фактически являются просто структурами дан-
ных, которые размещаются и используются ядром. В Windows структуры данных ядра
имеют много общего, так что очень полезно управлять ими неким унифицированным
способом.
Диспетчер объектов предоставляет следующие средства: управление размещением
и освобождением памяти для объектов, учет квот, поддержка доступа к объектам при
помощи описателей, подсчет ссылок на указатели в режиме ядра и ссылок на описа-
тели, именование объектов в пространстве имен NT, предоставление расширяемого
механизма для управления жизненным циклом любого объекта. Те структуры данных
ядра, которым нужны эти средства, управляются диспетчером объектов.
Каждый объект диспетчера объектов имеет тип, который используется для указания
того, как необходимо управлять жизненным циклом объекта данного типа. Это не тип
в объектно-ориентированном смысле — это просто коллекция параметров, указывае-
мых при создании типа объекта. Для создания нового типа компонент исполнительного
уровня просто вызывает API диспетчера объектов. Объекты занимают настолько важ-
ное место в функционировании Windows, что мы будем обсуждать диспетчер объектов
в следующем разделе более подробно.
Диспетчер ввода-вывода
(I/O manger) предоставляет инфраструктуру для реализации
драйверов устройств ввода-вывода и обеспечивает несколько служб исполнительного
уровня для конфигурирования устройств, доступа к ним и выполнения с ними опера-
ций. В Windows драйверы устройств не только управляют физическими устройствами,
они также обеспечивают расширяемость операционной системы. Многие функции, ко-
торые в других системах скомпилированы в состав ядра, в Windwos динамически загру-
жаются и связываются (в том числе стеки сетевых протоколов и файловые системы).
В новых версиях Windows имеется гораздо большая поддержка работы драйверов
устройств в пользовательском режиме (для новых драйверов устройств эта модель
является предпочтительной). Существуют сотни тысяч разных драйверов устройств
для Windows (работающих более чем с 1 млн различных устройств). Это огромное
количество кода, который должен работать правильно. Будет гораздо лучше, если
ошибка в процессе пользовательского режима приведет к недоступности устройства,
964
Глава 11. Изучение конкретных примеров: Windows 8
чем к полному отказу системы. Ошибки в драйверах устройств режима ядра являют-
ся основной причиной этих ужасных синих экранов смерти (Blue Screen Of Death
(BSOD)), возникающих, когда Windows обнаруживает фатальную ошибку в режиме
ядра и завершает работу системы. BSOD — это практически то же самое, что и паника
ядра (kernel panic) в системах UNIX.
По существу, компания Microsoft теперь официально признала то, что исследователи
микроядер (таких, как MINIX 3 и L4) знали уже много лет назад: чем больше кода
в ядре, тем больше в нем ошибок. Поскольку драйверы устройств составляют примерно
70 % кода ядра, то чем больше драйверов будет перенесено в процессы пользователь-
ского режима (где ошибка может привести только к сбою самого драйвера, а не к краху
всей системы), тем лучше. Тенденция переноса кода из ядра в процессы пользователь-
ского режима в ближайшие годы должна ускориться.
Диспетчер ввода-вывода содержит также средства для Plug-and-Play и управления
электропитанием. Plug-and-Play вступает в дело тогда, когда в системе обнаружива-
ются новые устройства. Сначала ставится в известность подкомпонент Plug-and-Play.
Он работает со службой (диспетчером Plug-and-Play пользовательского режима), ищет
соответствующий драйвер устройства и загружает его в систему. Найти правильный
драйвер устройства непросто, иногда для этого нужно выполнить сложное сопоставле-
ние конкретной версии аппаратного устройства и конкретной версии драйвера. Ино-
гда одно устройство поддерживает стандартный интерфейс, который поддерживается
множеством различных драйверов, написанных разными компаниями.
Ввод-вывод будет рассмотрен далее, в разделе 11.7, а наиболее важная файловая си-
стема NT—NTFS — в разделе 11.8.
Диспетчер электропитания по возможности снижает энергопотребление, увеличивая
время работы ноутбуков от батарей и сберегая энергию на настольных компьютерах
и серверах. Настройка управления электропитанием может быть непростой, посколь-
ку существует множество тонких различий между устройствами и шинами, которые
подключают их к процессору и памяти. Энергопотребление зависит не только от того,
какие устройства подключены к питанию, но и от тактовой частоты процессора, ко-
торый также управляется диспетчером электропитания. Более подробно управление
электропитанием будет рассмотрено в разделе 11.9.
Диспетчер процессов
(process manager) управляет созданием и завершением процессов
и потоков, включая настройку управляющих ими политик и параметров. Однако опера-
ционные аспекты потоков определяются уровнем ядра, который управляет планировани-
ем и синхронизацией потоков, а также их взаимодействием с управляющими объектами
(такими, как АРС). Процессы содержат потоки, адресное пространство, а также таблицу
описателей, содержащую те описатели, которые процесс может использовать для ссылки
на объекты режима ядра. Процессы также содержат информацию, которая нужна плани-
ровщику для переключения между адресными пространствами и для управления специ-
фичной для процессов информацией относительно оборудования (такой, как дескрип-
торы сегментов). Мы будем изучать управление процессами и потоками в разделе 11.4.
Диспетчер памяти
(memory manager) исполнительного уровня реализует архитектуру
виртуальной памяти с подкачкой по требованию. Он управляет отображением вирту-
альных страниц на физические фреймы страниц, управляет имеющимися физическими
фреймами, а также файлом подкачки на диске, используемым для хранения закрытых эк-
земпляров виртуальных страниц, которые уже не загружены в память. Диспетчер памяти
предоставляет также специальные средства для больших серверных приложений (таких,
11.3. Структура системы
965
как базы данных) и компоненты времени исполнения для языков программирования
(такие, как сборщики мусора). Мы будем изучать управление памятью в разделе 11.5.
Диспетчер кэширования
(cache manger) оптимизирует производительность ввода-вы-
вода в файловой системе (поддерживая кэш страниц файловой системы в виртуальном
адресном пространстве ядра). Диспетчер кэширования использует виртуально адресуе-
мое кэширование, то есть организует кэшированные страницы по их расположению в их
файлах. Это существенно отличается от кэширования физических блоков, как это делает-
ся в UNIX, где система поддерживает кэш физически адресуемых блоков дискового тома.
Управление кэшированием реализовано при помощи отображения файлов в память.
Реальное кэширование выполняется диспетчером памяти. Диспетчер кэширования
должен заботиться только о принятии решения о том, какую часть какого файла кэши-
ровать, обеспечивая своевременный сброс на диск кэшированных данных и управляя
виртуальными адресами ядра (используемыми для отображения кэшированных стра-
ниц файлов). Если нужная для ввода-вывода в файл страница в кэше отсутствует, то
при использовании диспетчера памяти будет получена ошибка отсутствия страницы.
Мы изучим диспетчер кэширования в разделе 11.6.
Монитор безопасности
(security reference monitor) обеспечивает работу сложных
механизмов безопасности Windows, которые поддерживают международные стан-
дарты по компьютерной безопасности, называемые Common Criteria (это развитие
требований по безопасности Оранжевой книги Министерства обороны США). В этих
стандартах содержится большое количество правил, которым должна соответствовать
система (таких, как аутентифицированный вход в систему, аудит, заполнение нулями
выделенной памяти и многое другое). Одно из правил требует, чтобы все проверки
доступа реализовывались по всей системе единым модулем. В Windows этим модулем
является монитор безопасности в ядре. Мы будем изучать систему безопасности более
подробно в разделе 11.10.
Исполнительный слой содержит и некоторые другие компоненты, которые мы кратко
опишем. Диспетчер конфигурации (configuration manager) — это компонент исполни-
тельного уровня, который реализует реестр (как уже описывалось). Реестр содержит
конфигурационные данные для системы в файлах, которые называются разделами.
Самым важным разделом является SYSTEM, который при загрузке грузится в па-
мять. Только после того как исполнительный уровень успешно инициализирует свои
основные компоненты (включая и те драйверы ввода-вывода, которые ведут обмен
с системным диском), находящаяся в памяти копия раздела вновь связывается со своей
копией в файловой системе. Таким образом, если при попытке загрузить систему про-
исходит что-то плохое, то находящаяся на диске копия имеет гораздо меньше шансов
получить повреждения.
Компонент LPC обеспечивает высокоэффективный межпроцессный обмен, исполь-
зуемый между работающими на одной системе процессами. Это один из транспортов
данных, используемых стандартным средством вызова удаленных процедур (RPC)
для реализации клиент-серверной модели вычислений. RPC использует в качестве
транспорта также именованные каналы и TCP/IP.
LPC в Windows 8 был существенно улучшен (получив новое название ALPC Advanced
LPC — расширенный LPC)), и теперь он обеспечивает поддержку новых функцио-
нальных возможностей RPC (в том числе RPC из компонентов режима ядра, таких
как драйверы). LPC был критичным компонентом в первоначальной структуре NT,
поскольку он используется уровнем подсистемы для реализации обмена между за-