Наверх Системное программирование
Предыдущий раздел Оглавление Следующий раздел

2.3.4. Подсистемы, DLL и службы пользовательского режима

Рассмотрим компоненты пользовательского режима, из которых для Windows особенно важны три типа: подсистемы среды, DLL и процессы служб.

В исходном проекте NT подсистемы рассматривались как способ поддержки образов нескольких операционных систем через единое базовое программное обеспечение, работающее в режиме ядра. Это было попыткой избежать соревнования операционных систем за одну и ту же платформу или неуверенности в существующем интерфейсе программирования. В любом Win32 API, разработанный для совместного использования с Windows 95, стал основным интерфейсом программирования.

Второй ключевой аспект проекта пользовательского режима Windows — это динамически связываемые библиотеки (Dynamic Link Library (DLL)), являющиеся кодом, который связывается с исполняемыми программами во время выполнения (а не во время компиляции). Совместно используемые библиотеки не являются новой концепцией, они используются большинством современных операционных систем. В Windows почти все библиотеки — это DLL, начиная с системной библиотеки ntdll.dll (которая загружается в каждый процесс) и заканчивая библиотеками высокого уровня (с обычными функциями), которые предназначены для того, чтобы сделать возможным интенсивное повторное использование кода разработчиками приложений.

DLL повышают эффективность системы (позволяя процессам совместно использовать код), снижают время загрузки программ с диска (часто используемый код хранится в памяти), увеличивают удобство эксплуатации системы (позволяя обновлять библиотечный код операционной системы без необходимости перекомпиляции или повторной сборки всех тех приложений, которые его используют).

Концепция реализации DLL проста. Вместо того чтобы компилятор выдавал код, который будет напрямую вызывать процедуры из того же исполняемого образа, вводится уровень косвенного обращения — таблица IAT (Import Address Table — таблица адресов импорта). Когда загружается исполняемый модуль, в нем производится поиск списка тех DLL, которые также должны загружаться (на самом деле это целый граф, потому что в этих DLL есть список других DLL, нужных для их работы). Необходимые DLL загружаются, и IAT заполняется для всех них.

Вся эта динамическая загрузка кода приводит к еще большему усложнению операционной системы, поскольку управление версиями библиотек — это не просто сопоставление исполняемых модулей и правильных версий DLL, иногда приходится загружать в процесс несколько версий одной и той же DLL. Одна программа может размещать две разные динамические библиотеки, причем они обе могут пожелать загрузить одну и ту же библиотеку Windows (и потребовать свою конкретную версию этой библиотеки).

Одна из причин того, что режим ядра проще, чем пользовательский режим, состоит в том, что он поддерживает относительно небольшое количество возможностей расширения помимо модели драйверов устройств. В Windows функциональность системы расширяется за счет служб пользовательского режима. Это решение хорошо работало для подсистем, и еще лучше оно работает тогда, когда реализуется всего несколько новых служб, а не полный образ операционной системы. Между службами, реализованными в процессах ядра и процессах пользовательского режима, имеется относительно немного различий. И ядро и процесс предоставляют частные адресные пространства, где структуры данных могут быть защищены и запросы служб могут отслеживаться.

Однако службы в процессах ядра и службы в пользовательских процессах могут существенно различаться по производительности. На современном оборудовании переход из режима ядра в пользовательский режим — процесс медленный, но он все же быстрее, чем два таких перехода (когда вам нужно переключиться в другой процесс и обратно). Кроме того, межпроцессный обмен имеет меньшую пропускную способность.

Код режима ядра может (с большой осторожностью) обращаться к данным по адресам пользовательского режима, передаваемым как параметры системных вызовов. Для служб пользовательского режима либо эти данные должны копироваться в процесс службы, либо нужно играть с отображением памяти туда и обратно (в Windows это делается при помощи средств ALPC).

Windows интенсивно использует процессы служб пользовательского режима для расширения функциональности системы. Некоторые из этих служб, строго привязаны к работе компонентов режима ядра. Таков lsass.exe, являющийся локальной службой аутентификации, управляющей объектами маркеров (которые представляют собой идентификацию пользователя), а также управляющий используемыми файловой системой ключами шифрования. Диспетчер Plug-and-Play пользовательского режима отвечает за определение правильного драйвера (который нужно использовать при обнаружении нового аппаратного устройства), его инсталляцию и выдачу ядру указания о его загрузке. Многие средства сторонних разработчиков (такие, как антивирусы и средства управления цифровыми правами) реализованы как комбинация драйверов режима ядра и служб пользовательского режима.

В Windows диспетчер задач taskmgr.exe имеет вкладку, в которой указаны работающие в системе службы. В одном процессе (svchost.exe) можно увидеть несколько работающих служб. Windows делает так для многих служб этапа загрузки (для уменьшения времени запуска системы). Службы можно комбинировать в одном процессе, если они могут безопасно работать с одинаковыми атрибутами системы безопасности.

Индивидуальные службы внутри любого совместно используемого процесса службы загружаются как DLL. Они обычно совместно используют пул потоков Win32, так что для всех резидентных служб требуется минимальное количество потоков.

Предыдущий раздел Оглавление Следующий раздел