"1.Внутреннее устройство Windows (гл. 1-4)" - читать интересную книгу автора (Руссинович Марк, Соломон Дэвид)ГЛABA 2 Архитектура системыТеперь, познакомившись с необходимыми терминами, понятиями и инструментами, мы можем рассмотреть задачи, которые ставились при разработке операционной системы Microsoft Windows. B этой главе описывается общая архитектура системы: ключевые компоненты, принципы их взаимодействия и контекст выполнения. Чтобы получить базовое представление о внутреннем устройстве Windows, давайте сначала обсудим требования и цели, обусловившие структуру и спецификацию этой системы. Характеристики Windows NT в 1989 году определялись следующими требованиями. Операционная система должна: • быть истинно 32-разрядной, реентерабельной, поддерживать вытесняющую многозадачность и работу с виртуальной памятью; • работать на разных аппаратных платформах; • хорошо масштабироваться в системах с симметричной мультипроцессорной обработкой; • быть распределенной вычислительной платформой, способной выступать в роли как клиента сети, так и сервера; • поддерживать большинство существующих 16-разрядных приложений MS-DOS и Microsoft Windows 3.1; • отвечать требованиям правительства к соответствию POSIX 1003.1; • отвечать требованиям правительства и промышленности к безопасности операционных систем; • обеспечивать простоту адаптации к глобальному рынку за счет поддержки Unicode. Для создания системы, соответствующей предъявленным требованиям, нужно было принять тысячи решений. Поэтому перед командой разработчиков Windows NT на начальном этапе проекта были поставлены следующие цели. • Расширяемость Код должен быть написан так, чтобы системы можно было легко наращивать и модифицировать по мере изменения потребностей рынка. • Переносимость Система должна работать на разных аппаратных архитектурах и обладать способностью к сравнительно легкому переносу на новые аппаратные архитектуры, если на рынке возникнет такая потребность. • Отказоустойчивость и надежность Система должна быть защищенной как от внутренних сбоев, так и от внешних деструктивных действий. У приложений не должно быть возможности нарушить работу операционной системы или других приложений. • Совместимость Хотя Windows NT должна расширить существующую технологию, ее пользовательский интерфейс и API должны быть совместимы с предыдущими версиями Windows и MS-DOS. Она также должна уметь взаимодействовать с другими системами вроде UNIX, OS/2 и NetWare. • Производительность C учетом ограничений, налагаемых поставленными целями, система должна быть максимально быстрой и отзывчивой независимо от аппаратной платформы. По мере изучения деталей внутренней структуры Windows вы увидите, насколько успешно были реализованы все эти требования и цели. Ho сначала мы рассмотрим общую модель Windows и сравним ее с другими современными операционными системами. B большинстве многопользовательских операционных систем приложения отделены от собственно операционной системы: код ее ядра выполняется в привилегированном режиме процессора (называемом Windows, как и большинство UNIX-систем, является монолитной операционной системой — в том смысле, что большая часть ее кода и драйверов использует одно и то же пространство защищенной памяти режима ядра. Это значит, что любой компонент операционной системы или драйвер устройства потенциально способен повредить данные, используемые другими компонентами операционной системы. Хотя некоторые объявляют ее таковой, Windows не является операционной системой на основе микроядра в классическом понимании этого термина. B подобных системах основные компоненты операционной системы (диспетчеры памяти, процессов, ввода-вывода) выполняются как отдельные процессы в собственных адресных пространствах и представляют собой надстройки над примитивными сервисами микроядра. Пример современной системы с архитектурой на основе микроядра — операционная система Mach, разработанная в Carnegie MeI-lon University. Она реализует крошечное ядро, которое включает сервисы планирования потоков, передачи сообщений, виртуальной памяти и драйверов устройств. Все остальное, в том числе разнообразные API, файловые системы и поддержка сетей, работает в пользовательском режиме. Однако в коммерческих реализациях на основе микроядра Mach код файловой системы, поддержки сетей и управления памятью выполняется в режиме ядра. Причина проста: системы, построенные строго по принципу микроядра, непрактичны с коммерческой точки зрения из-за слишком низкой эффективности. Означает ли тот факт, что большая часть Windows работает в режиме ядра, ее меньшую надежность в сравнении с операционными системами на основе микроядра? Вовсе нет. Рассмотрим следующий сценарий. Допустим, в коде файловой системы имеется ошибка, которая время от времени приводит к краху системы. Ошибка в коде режима ядра (например, в диспетчере памяти или файловой системы) скорее всего вызовет полный крах традиционной операционной системы. B истинной операционной системе на основе микроядра подобные компоненты выполняются в пользовательском режиме, поэтому теоретически ошибка приведет лишь к завершению процесса соответствующего компонента. Ho на практике такая ошибка все равно вызовет крах системы, так как восстановление после сбоя столь критически важного процесса невозможно. Все эти компоненты операционной системы, конечно, полностью защищены от сбойных приложений, поскольку такие программы не имеют прямого доступа к коду и данным привилегированной части операционной системы (хотя и способны вызывать сервисы ядра). Эта защита — одна из причин, по которым Windows заслужила репутацию отказоустойчивой и стабильной операционной системы в качестве сервера приложений и платформы рабочих станций, обеспечивающей быстродействие основных системных сервисов вроде поддержки виртуальной памяти, файлового ввода-вывода, работы с сетями и доступа к общим файлам и принтерам. Компоненты Windows режима ядра также построены на принципах объектно-ориентированного программирования (ООП). Так, для получения информации о каком-либо компоненте они, как правило, не обращаются к его структурам данных. Вместо этого для передачи параметров, доступа к структурам данных и их изменения используются формальные интерфейсы. Однако, несмотря на широкое использование объектов, представляющих разделяемые системные ресурсы, Windows не является объектно-ориентированной системой в строгом понимании этого термина. Большая часть системного кода написана на C в целях переносимости и из-за широкой распространенности средств разработки на С. B этом языке нет прямой поддержки конструкций и механизмов ООП вроде динамического связывания типов данных, полиморфных функций или наследования классов. Теперь обратимся к ключевым компонентам системы, составляющим ее архитектуру. Упрощенная версия этой архитектуры показана на рис. 2–1. Учтите, что упрощенная схема не отражает всех деталей архитектуры (например, здесь не показаны уровни сетевых компонентов и различных типов драйверов устройств). Рис. 2–1. Ha рис. 2–1 прежде всего обратите внимание на линию, разделяющую те части Windows, которые выполняются в режиме ядра и в пользовательском режиме. Прямоугольники над этой линией соответствуют процессам пользовательского режима, а компоненты под ней — сервисам режима ядра. Как говорилось в главе 1, потоки пользовательского режима выполняются в защищенных адресных пространствах процессов (хотя при выполнении в режиме ядра они получают доступ к системному пространству). Таким образом, процессы поддержки системы, сервисов, приложений и подсистем окружения имеют свое адресное пространство. Существует четыре типа пользовательских процессов: • фиксированные • • • Windows включает следующие компоненты режима ядра. • • • • • B таблице 2–1 перечислены имена файлов основных компонентов Windows. (Вы должны знать их, потому что в дальнейшем мы будем ссылаться на некоторые системные файлы по именам.) Каждый из этих компонентов подробно рассматривается в этой и последующих главах. Прежде чем детально рассматривать эти компоненты, давайте проясним, как достигается переносимость Windows между различными аппаратными платформами. Windows рассчитана на разные аппаратные платформы, включая как CISC-системы Intel, так и RISC-системы. Windows NT первого выпуска поддерживала архитектуры x86 и MIPS. Спустя некоторое время была добавлена поддержка Alpha AXP производства DEC (DEC была приобретена Compaq, а позднее произошло слияние компаний Compaq и Hewlett Packard). (Хотя Alpha AXP был 64-разрядным процессором, Windows NT работала с ним в 32-разрядном режиме. B ходе разработки Windows 2000 была создана ее 64-разрядная версия специально под Alpha AXP, но в свет она так и не вышла.) B Windows NT 3.51 ввели поддержку четвертой процессорной архитектуры — Motorola PowerPC. B связи с изменениями на рынке необходимость в поддержке MIPS и PowerPC практически отпала еще до начала разработки Windows 2000. Позднее Compaq отозвала поддержку архитектуры Alpha AXP, и в Windows 2000 осталась поддержка лишь архитектуры x86. B самые последние выпуски — Windows XP и Windows Server 2003 — добавлена поддержка трех семейств 64-разрядных процессоров: Intel Itanium IA-64, AMD x86-64 и Intel 64-bit Extension Technology (EM64T) для x86 (эта архитектура совместима с архитектурой AMD x86-64, хотя есть небольшие различия в поддерживаемых командах). Последние два семейства процессоров называются Переносимость Windows между системами с различной аппаратной архитектурой и платформами достигается главным образом двумя способами. • Windows имеет многоуровневую структуру. Специфичные для архитектуры процессора или платформы низкоуровневые части системы вынесены в отдельные модули. Благодаря этому высокоуровневая часть системы не зависит от специфики архитектур и аппаратных платформ. Ключевые компоненты, обеспечивающие переносимость операционной системы, — ядро (содержится в файле Ntoskrnl.exe) и уровень абстрагирования от оборудования (HAL) (содержится в файле Hal.dll). Функции, специфичные для конкретной архитектуры (переключение контекста потоков, диспетчеризация ловушек и др.), реализованы в ядре. Функции, которые могут отличаться на компьютерах с одинаковой архитектурой (например, в системах с разными материнскими платами), реализованы в HAL. Еще один компонент, содержащий большую долю кода, специфичного для конкретной архитектуры, — диспетчер памяти (memory manager), но если рассматривать систему в целом, такого кода все равно немного. • Подавляющее большинство компонентов Windows написано на C и лишь часть из них — на C++. Язык ассемблера применяли только при создании частей системы, напрямую взаимодействующих с системным оборудованием (например, при написании обработчика ловушек прерываний) или требующих исключительного быстродействия (скажем, при переключении контекста). Ассемблерный код имеется не только в ядре и HAL, но и в составе некоторых других частей операционной системы: процедур, реализующих взаимоблокировку, механизма вызова локальных процедур (LPC), части подсистемы Windows, выполняемой в режиме ядра, и даже в некоторых библиотеках пользовательского режима (например, в коде запуска процессов в Ntdll.dll — системной библиотеке, о которой будет рассказано в этой главе несколько позже). Как уже говорилось в начале этой главы, одной из ключевых целей разработки Windows была поддержка многопроцессорных компьютерных систем. Windows является операционной системой, поддерживающей Windows XP и Windows Server 2003 поддерживают два новых типа многопроцессорных систем: логические процессоры (hyperthreading) и NUMA (Non-Uniform Memory Architecture). Об этом кратко рассказывается в абзаце ниже. (Полное описание поддержки планирования потоков для таких систем см. в разделе по планированию потоков в главе 6.) Логические процессоры — это технология, созданная Intel; благодаря ей на одном физическом процессоре может быть несколько логических. Каждый логический процессор имеет свое состояние, но исполняющее ядро (execution engine) и набортный кэш (onboard cache) являются общими. Это позволяет одному из логических процессоров продолжать работу, пока другой логический процессор занят (например, обработкой прерывания, которая не дает потокам выполняться на этом логическом процессоре). Алгоритмы планирования в Windows XP были оптимизированы под компьютеры с такими процессорами. B NUMA-системах процессоры группируются в блоки, называемые узлами (nodes). B каждом узле имеются свои процессоры и память, и он соединяется с остальными узлами специальной шиной. Windows в NUMA-систе-ме по-прежнему работает как SMP-система, в которой все процессоры имеют доступ ко всей памяти, — просто доступ к памяти, локальной для узла, осуществляется быстрее, чем к памяти в других узлах. Система стремится повысить производительность, выделяя потокам время на процессорах, которые находятся в том же узле, что и используемая память. Она также пытается выделять память в пределах узла, но при необходимости выделяет память и из других узлов. Хотя Windows изначально разрабатывалась для поддержки до 32 процессоров, многопроцессорной модели не свойственны никакие внутренние особенности, которые ограничивали бы число используемых процессоров до 32. Просто это число легко представить битовой маской с помощью машинного 32-разрядного типа данных. И действительно, 64-разрядные версии Windows поддерживают до 64 процессоров, потому что размер слова на 64-разрядных процессорах равен 64 битам. Реальное число поддерживаемых процессоров зависит от конкретного выпуска Windows (см. таблицы 2–3 и 2–4). Это число хранится в параметре реестра HKLM\SYSTEM\CurrentControlSet\Control\Session\Manager\Licensed-Processors. Учтите, что модификация этого параметра считается нарушением условий лицензионного соглашения на программное обеспечение, да и для увеличения числа поддерживаемых процессоров требуется нечто большее, чем простое изменение данного параметра.) Для большей производительности ядро и HAL имеют одно- и многопроцессорную версии. B случае Windows 2000 это относится к шести ключевым системным файлам (см. примечание ниже), а в 32-разрядных Windows XP и Windows Server 2003 — только к трем (см. таблицу 2–2). B 64-разрядных системах Windows ядра PAE нет, поэтому одно- и многопроцессорные системы отличаются лишь ядром и HAL. Соответствующие файлы выбираются и копируются в локальный каталог \Windows\System32 на этапе установки. Чтобы определить, какие файлы были скопированы, см. файл \Windows\Repair\Setup.log, где перечисляются все файлы, копировавшиеся на локальный системный диск, и каталоги на дистрибутивном носителе, откуда они были взяты. ЭКСПЕРИМЕНТ: поиск файлов поддержки многопроцессорных систем в Windows Вы можете убедиться в том, что для многопроцессорной 32-разрядной системы Windows 2000 используются другие файлы, просмотрев сведения о драйверах для Computer (Компьютер) в Device Manager (Диспетчер устройств). 1. Откройте окно свойств системы, дважды щелкнув System (Система) в окне Control Panel (Панель управления) или щелкнув правой кнопкой мыши My Computer (Мой компьютер) на рабочем столе и выбрав из контекстного меню команду Properties (Свойства). 2. Перейдите на вкладку Hardware (Оборудование). 3. Щелкните кнопку Device Manager (Диспетчер устройств). 4. Раскройте объект Computer (Компьютер). 5. Дважды щелкните дочерний узел объекта Computer. 6. Откройте вкладку Driver (Драйвер). 7. Щелкните кнопку Driver Details (Сведения о драйверах). B многопроцессорной системе вы должны увидеть диалоговое окно, показанное ниже. Специальные версии этих ключевых системных файлов для однопроцессорных систем созданы для максимального повышения производительности. Синхронизация работы нескольких процессоров — задача принципиально более сложная, и благодаря «однопроцессорным» версиям системных файлов устраняются издержки этой синхронизации, которая в однопроцессорных системах (а они составляют подавляющее большинство систем под управлением Windows) не нужна. Интересно, что «однопроцессорная» и «многопроцессорная» версии Ntoskrnl создаются за счет условной компиляции одного и того же исходного кода, а «однопроцессорные» версии Ntdll.dll и Kernel32.dll для Windows 2000 требуют замены машинных х86-команд LOCK и UNLOCK, используемых для синхронизации множества потоков, командой NOP (которая ничего не делает). Остальные системные файлы Windows (включая все утилиты, библиотеки и драйверы устройств) одинаковы как в многопроцессорных, так и в однопроцессорных системах. При разработке нового программного обеспечения — Windows-приложения или драйвера устройства — вы должны учитывать этот подход и тестировать свое программное обеспечение как в одно-, так и в многопроцессорных системах. ЭКСПЕРИМЕНТ: определение текущей версии Ntoskrnl B Windows 2000 и выше нет утилиты, показывающей, с какой версией Ntoskrnl вы работаете. Однако при каждой загрузке в журнале системы регистрируется, какая версия ядра запускается — одно- или многопроцессорная, отладочная или конечная (см. следующую иллюстрацию). Выберите из меню Start (Пуск) команду Programs (Программы), затем Administrative Tools (Администрирование) и Event Viewer (Просмотр событий). Далее выберите System Log (Журнал системы) и дважды щелкните запись с кодом события 6009 — она создается при загрузке системы. Эта запись не содержит сведений о том, загружена ли РАЕ-версия образа ядра, поддерживающая более 4 Гб физической памяти (Ntkrnlpa.exe). Однако вы можете узнать это, проверив значение параметра SystemStartOptions в разделе реестра HKLM\SYSTEM\Current-ControlSet\Control. Кроме того, при загрузке РАЕ-версии ядра параметру PhysicalAddressExtension в разделе реестра HKLM\SYSTEM\Current-ControlSet\Control\Session Manager\Memory Management присваивается значение, равное 1. Есть и другой способ определить, установлена ли многопроцессорная версия Ntoskrnl (или Ntkrnlpa): запустите Windows Explorer (Проводник), в каталоге \Windows\System32 щелкните правой кнопкой мыши файл Ntoskrnl.exe и выберите из контекстного меню команду Pro- perties (Свойства). Перейдите на вкладку Version(Версия) и выберите свойство Original Filename (Исходное имя файла). Если вы работаете с многопроцессорной версией, то увидите диалоговое окно, показанное на предыдущей странице. Наконец, просмотрев файл \Windows\Repair\Setup.log, можно точно выяснить, какие файлы ядра и HAL были выбраны при установке. • способность выполнять код операционной системы на любом доступном процессоре и на нескольких процессорах одновременно; • несколько потоков одного процесса можно параллельно выполнять на разных процессорах; • тонкая синхронизация внутри ядра (спин-блокировки, спин-блокировки с очередями и др.; см. главу 3), драйверов устройств и серверных процессов позволяет выполнять больше компонентов на нескольких процессорах одновременно; • механизмы вроде портов завершения ввода-вывода (см. главу 9), облегчающие эффективную реализацию многопоточных серверных процессов, хорошо масштабируемых в многопроцессорных системах. Масштабируемость ядра Windows со временем улучшалась. Например, в Windows Server 2003 имеются очереди планирования, индивидуальные для каждого процессора, что дает возможность планировать потоки параллельно на нескольких машинах. O планировании потоков в многопроцессорных системах см. главу 6, а о синхронизации в таких системах — главу 3. Windows поставляется в клиентских и серверных версиях. B Windows 2000 клиентская версия называется Windows 2000 Professional. Существует также три серверных версии Windows 2000: Windows 2000 Server, Advanced Server и Datacenter Server. У Windows XP шесть клиентских версий: Windows XP Home Edition, Windows XP Professional, Windows XP Starter Edition, Windows XP Tablet PC Edition, Windows XP Media Center Edition и Windows XP Embedded. Последние три являются надмножествами Windows XP Professional и в книге детально не рассматриваются, так как все они построены на том же базовом коде, что и Windows XP Professional. Windows Server 2003 выпускается в шести разновидностях: Windows Server 2003 Web Edition, Standard Edition, Small Business Server, Storage Server, Enterprise Edition и Datacenter Edition. Эти версии различаются по следующим параметрам: • числу поддерживаемых процессоров; • объему поддерживаемой физической памяти; • возможному количеству одновременных сетевых соединений (например, в клиентской версии допускается максимум 10 одновременных соединений со службой доступа к общим файлам и принтерам); • наличием в выпусках Server сервисов, не входящих в Professional (например, служб каталогов, поддержкой кластеризации и многопользовательской службы терминала). Эти различия для Windows 2000 суммируются в таблице 2–3. Ta же информация, но применительно к Windows XP и Windows Server 2003 дана в таблице 2–4. Хотя существует несколько клиентских и серверных выпусков операционной системы Windows, у них общий набор базовых системных файлов, в том числе: ядро, Ntoskrnl.exe (а также версия РАЕ, Ntkrnlpa.exe), библиотеки HAL, драйверы, основные системные утилиты и DLL. Эти файлы идентичны для всех выпусков Windows 2000. Итак, если образ ядра для Windows 2000 Professional и Windows 2000 Server одинаков (и сходен для Windows XP и Windows Server 2003), то как же система определяет, какой именно выпуск загружается? Для этого она проверяет значения параметров ProductType и ProductSuite в разделе реестра HKLM\SYSTEM\CurrentControlSet\Control\ProductOptions. Параметр ProductType используется, чтобы отличить клиентскую систему от серверной (любого выпуска). Список допустимых значений этого параметра приведен в таблице 2–5. Результат проверки помещается в глобальную системную переменную Другой параметр, ProductSuite, позволяет различать серверные версии Windows (Standard, Enterprise, Datacenter Server и др.), а также Windows XP Home от Windows XP Professional. Для проверки текущего выпуска Windows пользовательские программы вызывают Windows-функцию Итак, если базовые файлы в целом одинаковы для клиентских и серверных версий, то чем же отличается их функционирование? Серверные системы оптимизированы для работы в качестве высокопроизводительных серверов приложений, а клиентские, несмотря на поддержку серверных возможностей, — для персональных систем. Так, некоторые решения по выделению ресурсов (например, о числе и размере системных пулов памяти, количестве внутрисистемных рабочих потоков и размере системного кэша данных) при загрузке принимаются по-разному, в зависимости от типа продукта. Политика принятия таких решений, как обслуживание диспетчером памяти запросов системы и процессов на выделение памяти, у серверной и клиентской версий тоже различается. B равной мере это относится и к особенностям планирования потоков по умолчанию (детали см. в главе 6). Существенные отличия в функционировании этих двух продуктов будут отмечены в соответствующих главах. Любые материалы в нашей книге, если явно не указано иное, относятся к обеим версиям — клиентской и серверной. Специальная отладочная версия Windows 2000 Professional, Windows XP Professional или Windows Server 2003 называется Проверочный выпуск предназначен главным образом разработчикам драйверов устройств, поскольку эта версия выполняет более строгую проверку ошибок при вызове функций режима ядра драйверами устройств или другим системным кодом. Например, если драйвер (или какой-то иной код режима ядра) неверно вызывает системную функцию, контролирующую передаваемые параметры, то при обнаружении этой проблемы система останавливается, предотвращая повреждение структур данных и возможный крах. ЭКСПЕРИМЕНТ: определяем, является ли данная система проверочным выпуском Встроенной утилиты, которая позволяла бы увидеть, с каким выпуском вы имеете дело — проверочным или готовым, нет. Однако эта информация доступна через свойство «Debug» WMI-класса Windows Management Instrumentation) Win32_OperatingSystem. Следующий сценарий на Visual Basic отображает содержимое этого свойства: Значительная часть дополнительного кода в собранных таким образом двоичных файлах является результатом работы макроса ASSERT, определенного в заголовочном файле Ntddk.h, который входит в состав DDK. Этот макрос проверяет некое условие (например, правильность структуры данных или параметра) и, если значение выражения получается равным FALSE, вызывает функцию ASSERT, выполняемых некоторыми вспомогательными процедурами ядра, см. в разделе «Checked Build ASSERTs» документации Windows DDK. Проверочный выпуск также полезен системным администраторам, так как в нем можно включить детальную трассировку для определенных компонентов. (Подробные инструкции см. в статье 3H743 «HOWTO: Enable Verbose Debug Tracing in Various Drivers and Subsystems» в Microsoft Knowledge Base.) Вывод такой трассировки посылается в буфер отладочных сообщений с помощью функции DbgPrint, о которой мы уже упоминали. Для просмотра отладочных сообщений к целевой системе можно подключить отладчик ядра (что потребует загрузки целевой системы в отладочном режиме), использовать команду Для использования возможностей отладочной версии операционной системы необязательно устанавливать весь проверочный выпуск. Можно просто скопировать проверочную версию образа ядра (Ntoskrnl.exe) и соответствующий HAL (Hal.dll) в обычную систему. Преимущество этого подхода в том, что он позволяет тщательно протестировать драйверы устройств и другой код ядра, не устанавливая медленнее работающие версии всех компонентов системы. O том, как это сделать, см. раздел «Installing Just the Checked Operating System and HAL» в документации Windows DDK. Поскольку Microsoft не поставляет проверочный выпуск Windows 2000 Server, вы можете применить этот способ и получить проверочную версию ядра в системе Windows 2000 Server. Наконец, проверочная версия пригодится и для тестирования кода пользовательского режима, но только в том смысле, что в проверочной версии системы устанавливаются другие интервалы ожидания (тайминги). (Это связано с тем, что в ядре выполняются дополнительные проверки, а сами компоненты компилируются без оптимизации.) B таких условиях часто проявляются ошибки, связанные с синхронизацией нескольких потоков приложения. Теперь, ознакомившись с высокоуровневой архитектурой Windows, копнем поглубже и рассмотрим роль каждого ключевого компонента системы. Ha рис. 2–3 отражена более подробная схема системной архитектуры Windows. Заметьте, что на ней все равно не показаны некоторые компоненты (в частности, компоненты сетевой поддержки, о которых пойдет речь в главе 13). Основные элементы этой схемы детально описываются в последующих главах. B главе 3 рассказывается об основных механизмах управления, используемых системой (в том числе о диспетчере объектов, прерываниях и т. п.), в главе 5 — о процессах запуска и завершения Windows, а в главе 4 — о таких механизмах управления, как реестр, процессы сервисов и Windows Management Instrumentation (WMI). B остальных главах не менее подробно поясняется внутреннее устройство и функционирование ключевых элементов — процессов, потоков, подсистемы управления памятью, защиты, диспетчера ввода-вывода, диспетчера кэша, файловой системы NTFS, сетевой поддержки и др. Рис. 2–3 Как показано на рис. 2–3, в Windows имеется три подсистемы окружения: OS/2, POSIX и Windows. Как мы уже говорили, подсистема OS/2 была удалена в Windows 2000. Начиная с Windows XP, базовая подсистема POSIX не поставляется с Windows, но ее гораздо более совершенную версию можно получить бесплатно как часть продукта Services for UNIX. Подсистема Windows отличается от остальных двух тем, что без нее Windows работать не может (эта подсистема обрабатывает все, что связано с клавиатурой, мышью и экраном, и нужна даже на серверах в отсутствие интерактивных пользователей). Фактически остальные две подсистемы запускаются только по требованию, тогда как подсистема Windows работает всегда. Стартовая информация подсистемы хранится в разделе реестра HKLM\ SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems. Значения параметров в этом разделе показаны на рис. 2–4. Значением параметра Required является список подсистем, загружаемых при запуске системы. Параметр состоит из двух строк: Windows и Debug. B параметре Windows указывается спецификация файла подсистемы Windows, Csrss.exe (аббревиатура от Client/Server Run-Time Subsystem; см. примечание ниже). Параметр Debug остается незаполненным (он используется для внутреннего тестирования) и не выполняет никаких функций. Параметр Optional указывает, что подсистемы OS/2 и POSIX запускаются по требованию. Параметр Kmode содержит имя файла той части подсистемы Windows, которая работает в режиме ядра, — Win32k.sys (об этом файле чуть позже). Подсистемы окружения предоставляют прикладным программам некое подмножество базовых сервисов исполнительной системы Windows. Каждая подсистема обеспечивает доступ к разным подмножествам встроенных сервисов Windows. Это значит, что приложения, созданные для одной подсистемы, могут выполнять операции, невозможные в другой подсистеме. Так, Windows-приложения не могут использовать POSIX-функцию fork. Каждый исполняемый образ (EXE) принадлежит одной — и только одной — подсистеме. При запуске образа код, отвечающий за создание процесса, получает тип подсистемы, указанный в заголовке образа, и уведомляет соответствующую подсистему о новом процессе. Тип указывается спецификатором /SUBSYSTEM в команде Смешивать вызовы функций разных подсистем нельзя. Иными словами, приложения POSIX могут вызывать только сервисы, экспортируемые подсистемой POSIX, а приложения Windows — лишь сервисы, экспортируемые подсистемой Windows. Как вы еще убедитесь, это ограничение послужило одной из причин, по которой исходная подсистема POSIX, реализующая весьма ограниченный набор функций (только POSIX 1003.1), не стала полезной средой для переноса в нее UNIX-приложений. Мы уже говорили, что пользовательские приложения не могут вызывать системные сервисы Windows напрямую. Вместо этого они обращаются к DLL подсистем. Эти DLL предоставляют документированный интерфейс между программами и вызываемой ими подсистемой. Так, DLL подсистемы Windows (Kernel32.dll, Advapi32.dll, User32.dll и Gdi32.dll) реализуют функции Windows API. DLL подсистемы POSIX (Psxdll.dll) реализует POSIX API. ЭКСПЕРИМЕНТ: определение типа подсистемы, для которой предназначен исполняемый файл Вы можете определить, для какой подсистемы предназначен исполняемый файл с помощью утилиты Exetype из набора ресурсов Windows или утилиты DependencyWalker (Depends.exe), входящей в состав Windows Support Tools и Platform SDK. Попробуем, например, выяснить тип подсистемы для двух принципиально разных Windows-образов: Notepad.exe (простого текстового редактора) и Cmd.exe (поддержки командной строки Windows). Это показывает, что Notepad является GUI-программой, a Cmd — консольной, или программой текстового режима. Хотя вывод утилиты Exetype сообщает о наличии двух разных подсистем для GUI- и консольных программ, на самом деле существует лишь одна подсистема Windows. Кроме того, Windows не поддерживает процессор Intel 386 (или 486, если это имеет какое-то значение) — текст сообщений, выводимых программой Exetype, просто не обновили. При вызове приложением одной из функций DLL подсистемы возможно одно из трех. • Функция полностью реализована в пользовательском режиме внутри DLL подсистемы. Иначе говоря, никаких сообщений процессу подсистемы окружения не посылается, и вызова сервисов исполнительной системы Windows не происходит. После выполнения функции в пользовательском режиме результат возвращается вызвавшей ее программе. Примерами таких функций могут служить • Функция требует одного или более вызовов исполнительной системы Windows. Например, Windows-функции • Функция требует выполнения каких-либо операций в процессе подсистемы окружения (такие процессы, работающие в пользовательском режиме, отвечают за обслуживание клиентских приложений, выполняемых под их контролем). B этом случае подсистеме окружения выдается клиент-серверный запрос через сообщение с требованием выполнить какую-либо операцию, и DLL подсистемы, прежде чем вернуть управление вызвавшей программе, ждет соответствующего ответа. Некоторые функции вроде Хотя структура Windows позволяет поддерживать несколько независимых подсистем окружения, с практической точки зрения было бы неудобно включать в состав каждой подсистемы свой код для обработки окон и отображения ввода-вывода. Это привело бы к дублированию системных функций и в конечном счете негативно отразилось бы на объеме и производительности системы. Поскольку главной подсистемой была Windows, разработчики решили разместить эти базовые функции именно в ней. Так что другие подсистемы для отображения ввода-вывода вызывают соответствующие сервисы Windows. (Кстати, посмотрев на тип подсистемы в заголовках их файлов, вы убедитесь, что фактически они являются исполняемыми файлами Windows.) Теперь поближе познакомимся с каждой подсистемой окружения. Эта подсистема состоит из следующих основных элементов. • Процесса подсистемы окружения (Csrss.exe), предоставляющего: • поддержку консольных (текстовых) окон; • поддержку создания и удаления процессов и потоков; • частичную поддержку процессов 16-разрядной виртуальной DOS-машины (VDM); • множество других функций, например • Драйвера режима ядра (Win32k.sys), включающего: • диспетчер окон, который управляет отрисовкой и выводом окон на экран, принимает ввод с клавиатуры, мыши и других устройств, а также передает пользовательские сообщения приложениям; • Graphics Device Interface (GDI), который представляет собой библиотеку функций для устройств графического вывода. B GDI входят функции для манипуляций с графикой и отрисовки линий, текста и фигур. • DLL-модулей подсистем (Kernel32.dll, Advapi32.dll, User32.dll и Gdi32.dll), транслирующих вызовы документированных функций Windows API в вызовы соответствующих (и в большинстве своем недокументированных) сервисов режима ядра из Ntoskrnl.exe и Win32k.sys. • Драйверов графических устройств, представляющих собой специфичные для конкретного оборудования драйверы графического дисплея, принтера и минипорт-драйверы видеоплат. Для формирования элементов управления пользовательского интерфейса на экране, например окон и кнопок, приложения могут вызывать стандартные функции USER. Диспетчер окон передает эти вызовы GDI, а тот — драйверам графических устройств, где они форматируются для дисплея. Драйвер дисплея работает в паре с соответствующим минипорт-драйвером видеоплаты, обеспечивая полную поддержку видео. GDI предоставляет набор стандартных функций двухмерной графики, которые позволяют приложениям, не имеющим представления о графических устройствах, обращаться к ним. GDI-функции играют роль посредника между приложениями и драйверами дисплея и принтера. GDI интерпретирует запросы приложений на вывод графики и посылает соответствующие запросы драйверам. Он также предоставляет приложениям стандартный унифицированный интерфейс для использования самых разнообразных устройств графического вывода. Этот интерфейс обеспечивает независимость кода приложений от конкретного оборудования и его драйверов. GDI выдает свои запросы с учетом возможностей конкретного устройства, часто разделяя запрос на несколько частей для обработки. Так, некоторые устройства сами умеют формировать эллипсы, а другие требуют от GDI интерпретировать эллипсы как набор пикселов с определенными координатами. Подробнее об архитектуре подсистемы вывода графики и драйвере дисплея см. раздел «Design Guide» в книге «Graphics Drivers» из Windows DDK. До Windows NT 4 диспетчер окон и графические сервисы были частью процесса подсистемы Windows пользовательского режима. B Windows NT 4 основная часть кода, ответственного за обработку окон и графики, перенесена из контекста процесса подсистемы Windows в набор вызываемых сервисов, выполняемых в режиме ядра (в файл Win32k.sys). Этот перенос был осуществлен в основном для повышения общей производительности системы. Отдельный серверный процесс, содержащий графическую подсистему, требовал многочисленных переключений контекста потоков и процессов, что отнимало большое количество тактов процессора и значительные ресурсы памяти, даже несмотря на высокую оптимизацию исходной архитектуры этой подсистемы. Например, каждый клиентский поток обслуживается парным серверным потоком в процессе подсистемы Windows, ожидающем запросов от клиентского потока. Для передачи сообщений между потоками используется специальный механизм взаимодействия между процессами, так называемый быстрый LPC (fast LPC). B отличие от обычного переключения контекста потоков передача данных между парными потоками через быстрый LPC не вызывает в ядре события перепланирования, что позволяет серверному потоку выполняться в течение оставшегося кванта времени клиентского потока (вне очереди, определенной планировщиком). Более того, для быстрой передачи больших структур данных, например битовых карт, используются разделяемые буферы памяти, и клиенты получают прямой доступ (только для чтения) к ключевым структурам данных сервера, а это сводит к минимуму необходимость в частом переключении контекста между клиентами и сервером Windows. GDI-операции выполняются в пакетном режиме. При этом серия графических объектов, запрошенных Windows-приложениями, не обрабатывается сервером и не прорисовывается на устройстве вывода до тех пор, пока не будет заполнена вся очередь GDI. Размер очереди можно установить через Windows-функцию Однако, несмотря на такую оптимизацию, общая производительность системы по-прежнему не соответствовала требованиям приложений, интенсивно работающих с графикой. Очевидным решением проблемы стал перевод подсистемы поддержки окон и графики в режим ядра, что позволило избежать потребности в дополнительных потоках и связанных с ними переключениями контекста. Кроме того, как только приложения вызывают диспетчер окон и GDI, эти подсистемы теперь получают прямой доступ к компонентам исполнительной системы Windows без перехода из пользовательского режима в режим ядра и обратно. Прямой доступ особенно важен в случае вызова GDI через видеодрайверы, когда взаимодействие с видеооборудованием требует высокой пропускной способности. Так что же остается в той части процесса подсистемы Windows, которая работает в пользовательском режиме? Поскольку консольные программы не перерисовывают окна, все операции по отрисовке и обновлению консольных и текстовых окон проводятся именно этой частью Windows. Увидеть ее деятельность несложно: просто откройте окно командной строки и перетащите поверх него другое окно. Вы увидите, что процесс подсистемы Windows начинает расходовать процессорное время, перерисовывая консольное окно. Кроме поддержки консольных окон, только небольшая часть Windows-функций посылает сообщения процессу подсистемы Windows. K ним относятся функции, отвечающие за создание и завершение процессов и потоков, назначение букв сетевым дискам, создание временных файлов. Как правило, Windows-приложение нечасто переключает (если вообще переключает) контекст в процесс подсистемы Windows. Некоторые интересуются, не повлияет ли на стабильность системы перевод такой значительной части кода в режим ядра. Ho риск снижения стабильности системы минимален. Дело в том, что до Windows NT 4 (равно как и в настоящее время) ошибка вроде нарушения доступа (access violation) в процессе подсистемы Windows пользовательского режима (Csrss.exe) приводила к краху системы, потому что процесс подсистемы Windows был и остается жизненно важным для функционирования всей системы. Поскольку структуры данных, определяющие окна на экране, содержатся именно в этом процессе, его гибель приводит к уничтожению пользовательского интерфейса. Однако даже при функционировании Windows в качестве сервера без интерактивных процессов система не могла бы работать без Csrss, поскольку серверные процессы иногда используют оконные сообщения для контроля внутреннего состояния приложений. Так что в Windows ошибки вроде нарушения доступа в том же коде, только выполняемом в режиме ядра, просто быстрее приводят к краху — исключения в режиме ядра требуют прекращения работы системы. Правда, теоретически появляется другая опасность. Поскольку этот код выполняется в режиме ядра, ошибка (например, применение неверного указателя) может повредить защищенные структуры данных режима ядра. До Windows NT 4 это могло привести к нарушению доступа, так как запись в страницы режима ядра из пользовательского режима не разрешается. Ho результатом стал бы крах системы. Теперь же при выполнении кода в режиме ядра запись на какую-либо страницу памяти по неверному указателю не обязательно вызовет немедленный крах системы. Ho, если при этом будут повреждены какие-то структуры данных, крах скорее всего произойдет. Тем не менее возникает риск, что из-за такого указателя будет повреждена не структура данных, а буфер памяти, и это приведет к возврату пользовательской программе или записи на диск неверных данных. Существует еще одно негативное последствие перевода графических драйверов в режим ядра. Ранее некоторые части графического драйвера выполнялись в Csrss, а остальные части — в режиме ядра. Теперь весь драйвер работает только в режиме ядра. Так как не все драйверы поддерживаемых Windows графических устройств разрабатываются Microsoft, она тесно сотрудничает с производителями оборудования, чтобы гарантировать разработку ими надежных и эффективных драйверов. Все поставляемые с системой драйверы тестируются так же тщательно, как и другие компоненты исполнительной системы. Наконец, важно понимать, что такая схема (при которой подсистема поддержки окон и графики выполняется в режиме ядра) не является принципиально рискованной. Идентичный подход используется для многих других драйверов устройств (например, сетевых карт и жестких дисков). Все эти драйверы, выполняемые в режиме ядра, никогда не снижали надежности Windows NT Некоторые распространяют измышления насчет снижения эффективности вытесняющей многозадачности Windows из-за перевода диспетчера окон и GDI в режим ядра. Теория, которая стоит за этой точкой зрения, — увеличивается время, затрачиваемое на дополнительную обработку Windows в режиме ядра. Это мнение возникло в результате ошибочного понимания архитектуры Windows. Действительно, во многих других операционных системах, формально поддерживающих вытесняющую многозадачность, планировщик никогда не вытесняет потоки, выполняемые в режиме ядра, или вытесняет, но лишь в отдельных ситуациях. Однако в Windows любые потоки, выполняемые в режиме ядра, планируются и вытесняются так же, как и потоки пользовательского режима, — код исполнительной системы полностью реентерабелен. Помимо многих других соображений, это просто необходимо для достижения высокого уровня масштабируемости системы на оборудовании с поддержкой SMR. Другое направление спекуляций касалось снижения масштабируемости SMP в результате уже описанных изменений. Теоретические обоснования были такими: раньше во взаимодействии между приложением и диспетчером окон или GDI участвовали два потока-, один — в приложении и один — в Csrss.exe. Поэтому в SMP-системах, где эти потоки могут выполняться параллельно, пропускная способность возрастает. Это свидетельствует о непонимании технологий, применявшихся до Windows NT 4. B большинстве случаев клиентские приложения вызывают процесс подсистемы Windows синхронно, т. е. клиентский поток полностью блокируется в ожидании обработки вызова серверным потоком и возобновляется только после этого. Так что никакой параллелизм в SMP-системах недостижим. Это явление легко наблюдать в SMP-системах на примере приложений, интенсивно работающих с графикой. При этом обнаружится, что в двухпроцессорной системе каждый процессор загружен на 50 %; также легко заметить единственный поток Csrss, отделенный от потока приложения. Действительно, поскольку два потока тесно взаимодействуют и находятся в сходном состоянии, для поддержания синхронизации процессорам приходится постоянно сбрасывать кэш. Именно по этой причине однопоточные графические приложения в SMP-системах под управлением Windows NT 3.51 обычно выполняются медленнее, чем в однопроцессорных системах. B результате изменений, внесенных в Windows NT 4, удалось повысить пропускную способность SMP-систем для приложений, интенсивно использующих диспетчер окон и GDI, — особенно когда в приложении работает более одного потока. При наличии двух потоков приложения на двухпроцессорной машине под управлением Windows NT 3.51 за процессорное время конкурируют в общей сложности четыре потока (два — в приложении и два — в Csrss). Хотя в каждый момент к выполнению готовы, как правило, лишь два потока, их рассогласованность ведет к потере локальности ссылок и синхронизации кэша. Это происходит скорее всего из-за переключения потоков приложения с одного процессора на другой. B Windows NT 4 каждый из двух потоков приложения по сути имеет собственный процессор, а механизм автоматической привязки потоков в Windows пытается постоянно выполнять данный поток на одном и том же процессоре, максимально увеличивая локальность ссылок и сводя к минимуму потребность в синхронизации кэш-памяти индивидуальных процессоров. B заключение отметим, что повышение производительности в результате перевода диспетчера окон и GDI из пользовательского режима в режим ядра достигнуто без сколько-нибудь значимого снижения стабильности и надежности системы — даже в случае нескольких сеансов, созданных в конфигурации с поддержкой Terminal Services. POSIX, название которой представляет собой аббревиатуру от «portable operating system interface based on UNIX» (переносимый интерфейс операционной системы на основе UNIX), — это совокупность международных стандартов на интерфейсы операционных систем типа UNIX. Стандарты POSIX стимулировали производителей поддерживать совместимость реализуемых ими UNIX-подобных интерфейсов, тем самым позволяя программистам легко переносить свои приложения между системами. B Windows реализован лишь один из многих стандартов POSIX, а именно POSIX.l, который официально называется ISO/IEC 9945-1:1990, или IEEE POSIX стандарта 1003 1-1990. Этот стандарт изначально был включен в основном для соответствия требованиям правительства США, установленным во второй половине 80-х годов. B федеральном стандарте Federal Information Processing Standard (FIPS) 151-2, разработанном государственным институтом стандартов и технологий (NIST), содержится требование совместимости с POSIX l.Windows NT 3.5, 3.51 и 4 прошли тестирование на соответствие FIPS 151-2. Поскольку совместимость с POSIX. 1 была одной из обязательных целей, в Windows включена необходимая базовая поддержка подсистемы POSIX1 — например, функция fork, реализованная в исполнительной системе Windows, и поддержка файловой системой Windows жестких файловых связей (hard file links). Однако POSIX.l определяетлишь ограниченный набор сервисов (управление процессами, взаимодействие между процессами, простой символьный ввод-вывод и т. д.), и поэтому подсистема POSIX в Windows не является полноценной средой программирования. Так как вызов функций из разных подсистем Windows невозможен, набор функций, доступный приложениям POSIX по умолчанию, строго ограничен сервисами, определяемыми POSIX1. Смысл этих ограничений в следующем: приложение POSIX не может создать поток или окно в Windows, а также использовать RPC или сокеты. Для преодоления этого ограничения предназначен продукт Microsoft Windows Services for UNIX, включающий (в версии 3.5) улучшенную подсистему окружения POSIX, которая предоставляет около 2000 функций UNIX и 300 инструментов и утилит в стиле UNIX. (Детали см. на Эта улучшенная подсистема POSIX реально помогает переносить UNIX-приложения в Windows. Однако, поскольку эти программы все равно связаны с исполняемыми файлами POSIX, Windows-функции им недоступны. Чтобы UNIX-приложения, переносимые в Windows, могли использовать Windows-функции, нужно приобрести специальные пакеты для переноса UNIX-программ в Windows, подобные продуктам MKS Toolkit, разработанные компанией Mortice Kern Systems Inc. ЭКСПЕРИМЕНТ: наблюдаем старт подсистемы POSIX Подсистема POSIX по умолчанию сконфигурирована на запуск в момент начала выполнения приложения POSIX. Таким образом, старт подсистемы POSIX можно наблюдать, запустив какую-нибудь программу POSIX, например одну из утилит POSIX из Windows Services for UNIX (небольшой набор утилит вы найдете и в каталоге \Apps\POSIX на компакт-диске ресурсов Windows 2000; они не устанавливаются как часть ресурсов). Для запуска подсистемы POSIX следуйте инструкциям, приведенным ниже. 1. Откройте окно командной строки. 2. Запустите Process Explorer и убедитесь, что подсистема POSIX еще не запущена (т. е. процесса Psxss.exe в системе нет). Также убедитесь, что Process Explorer отображает список процессов как дерево (нажмите Ctrl+T). 3. Запустите POSIX-программу (например C Shell или Korn Shell, поставляемую с Windows Services for UNIX) или утилиту POSIX из ресурсов Windows 2000, например \Apps\POSIX\Ls.exe. 4. Вернитесь в Process Explorer и обратите внимание на новый процесс Psxss.exe, являющийся дочерним процессом Smss.exe (который в зависимости от выбранного интервала подсветки может какое-то время оставаться выделенным как новый процесс). Для компиляции и сборки приложения POSIX в Windows нужны заголовочные файлы и библиотеки POSIX из Platform SDK. Исполняемые файлы POSIX связываются с библиотекой подсистемы POSIX, Psxdll.dll. Поскольку Windows по умолчанию сконфигурирована на запуск подсистемы POSIX только по требованию, при первом запуске приложения POSIX должен запуститься процесс подсистемы POSIX (Psxss.exe). Его выполнение продолжается до перезагрузки системы. (Если вы завершите процесс подсистемы POSIX, запуск приложений POSIX станет невозможен до следующей перезагрузки системы.) Приложение POSIX не выполняется самостоятельно; для него запускается специальный файл поддержки Posix.exe, создающий дочерний процесс, из которого и запускаются приложения POSIX. Подсистема окружения OS/2, как и подсистема POSIX, обладает довольно ограниченной функциональностью и поддерживает лишь 16-разрядные приложения OS/2 версии 1.2 с символьным или графическим вводом-выводом. Кроме того, Windows запрещает прикладным программам прямой доступ к оборудованию и поэтому не поддерживает приложения OS/2, использующие расширенный ввод-вывод видео или включающие сегменты привилегированного ввода-вывода, которые пытаются выполнять инструкции IN/OUT (для доступа к некоторым аппаратным устройствам). Приложения, выдающие машинные команды CLI/STI, могут работать в Windows, но на время выполнения команды STI все другие приложения OS/2 в системе и потоки процессов OS/2, выдающих команды CLI, приостанавливаются. Как показано на рис. 2–5, подсистема OS/2, использующая 32-разрядное виртуальное адресное пространство Windows, может предоставить приложениям OS/2 версии 1.2 до 512 Мб памяти, снимая тем самым исходное ограничение этой версии на объем адресуемой памяти (до 16 Мб). Как будет детально показано в главе 6, потоки являются элементами выполняемой программы и, как таковые, подлежат планированию (подключению к процессору по определенной схеме). B OS/2 всего 64 уровня приоритетов (от 0 до 63), а в Windows — 32 (от 0 до 31). Несмотря на это, 64 уровня приоритетов OS/2 проецируются на динамические приоритеты Windows с 1-го по 15-й. Потоки OS/2, выполняемые в Windows, никогда не получают приоритеты реального времени (16–31). Как и подсистема POSIX, подсистема OS/2 автоматически запускается при первой активизации OS/2-совместимого образа и продолжает выполняться до перезагрузки всей системы. Подробнее о выполнении приложений POSIX и OS/2 в Windows см. главу 6. Ntdll.dll — специальная библиотека системной поддержки, нужная в основном при использовании DLL подсистем. Она содержит функции двух типов: • интерфейсы диспетчера системных сервисов (system service dispatch stubs) к сервисам исполнительной системы Windows; • внутренние функции поддержки, используемые подсистемами, DLL подсистем и другими компонентами операционной системы. Первая группа функций предоставляет интерфейс к сервисам исполнительной системы Windows, которые можно вызывать из пользовательского режима. Таких функций более 200, например Для каждой из этих функций в Ntdll существует точка входа с тем же именем. Код внутри функции содержит специфичную для конкретной аппаратной архитектуры команду перехода в режим ядра для вызова диспетчера системных сервисов (о нем рассказывается в главе 3), который после проверки некоторых параметров вызывает уже настоящий сервис режима ядра из Ntoskrnl.exe. Ntdll также включает множество функций поддержки, например загрузчик образов (функции, имена которых начинаются с Исполнительная система (executive) находится на верхнем уровне Ntoskrnl.exe (ядро располагается на более низком уровне). B ее состав входят функции следующего типа. • Экспортируемые функции, доступные для вызова из пользовательского режима. Эти функции называются • Функции драйверов устройств, вызываемые через функцию • Экспортируемые функции, доступные для вызова только из режима ядра и документированные в Windows DDK или Windows Installable File System (IFS) Kit (см. • Экспортируемые функции, доступные для вызова только из режима ядра, но не описанные в Windows DDK или IFS Kit (например, функции, которые используются видеодрайвером, работающим на этапе загрузки, и чьи имена начинаются с • Функции, определенные как глобальные, но не экспортируемые символы. Включают внутренние функции поддержки, вызываемые в Ntoskrnl; их имена начинаются с • Внутренние функции в каком-либо модуле, не определенные как глобальные символы. Исполнительная система состоит из следующих основных компонентов (каждый из них подробно рассматривается в последующих главах книги). • • • • • • • WMI пользовательского режима. Потребители информации WMI могут находиться как на локальной машине, так и на любом компьютере в сети. • • • • • • Большой набор стандартных библиотечных функций для обработки строк, арифметических операций, преобразования типов данных и обработки структур безопасности. • Ядро состоит из набора функций в Ntoskrnl.exe, предоставляющих фундаментальные механизмы (в том числе планирования потоков и синхронизации), которые используются компонентами исполнительной системы и низкоуровневыми аппаратно-зависимыми средствами поддержки (диспетчеризации прерываний и исключений), различными в каждой процессорной архитектуре. Код ядра написан в основном на С, а ассемблер использовали лишь для решения специфических задач, трудно реализуемых на С. Как и функции поддержки исполнительной системы, упоминавшиеся в предыдущем разделе, часть функций ядра описана в DDK (см. функции, чьи имена начинаются с Ядро состоит из низкоуровневых, четко определенных и хорошо предсказуемых примитивов и механизмов операционной системы, позволяющих компонентам исполнительной системы более высокого уровня выполнять свои функции. Ядро отделено от остальной части исполнительной системы; оно реализует системные механизмы и не участвует в принятии решений, связанных с системной политикой. Практически все такие решения, кроме планирования и диспетчеризации потоков, принимаются исполнительной системой. Вне ядра исполнительная система представляет потоки и другие разделяемые ресурсы в виде объектов. Управление этими объектами требует определенных издержек, так как нужны описатели, позволяющие манипулировать объектами, средства защиты и квоты ресурсов, резервируемых при их создании. B ядре можно избежать таких издержек, поскольку оно реализует набор более простых объектов, называемых Одна из групп объектов ядра, Другая группа объектов под названием Другая важная задача ядра — абстрагирование или изоляция исполнительной системы и драйверов устройств от различий между аппаратными архитектурами, поддерживаемыми Windows (т. е. различий в обработке прерываний, диспетчеризации исключений и синхронизации между несколькими процессорами). Архитектура ядра нацелена на максимальное обобщение кода — даже в случае аппаратно-зависимых функций. Ядро поддерживает набор семантически идентичных и переносимых между архитектурами интерфейсов. Большая часть кода, реализующего переносимые интерфейсы, также идентична для разных архитектур. Ho одна часть этих интерфейсов по-разному реализуется на разных архитектурах, а другая включает код, специфичный для конкретной архитектуры. Архитектурно-независимые интерфейсы могут быть вызваны на любой машине, причем семантика интерфейса будет одинаковой — независимо от специфического кода для той или иной архитектуры. Некоторые интерфейсы ядра (например, процедуры спин-блокировки, описанные в главе 3) на самом деле реализуются в HAL (см. следующий раздел), поскольку их реализация может отличаться даже в пределах семейства процессоров с одинаковой архитектурой. B ядре также содержится небольшая порция кода с х86-специфичными интерфейсами, необходимыми для поддержки старых программ MS-DOS. Эти интерфейсы не являются переносимыми в том смысле, что их нельзя вызывать на машине с другой архитектурой, где они попросту отсутствуют. Этот х86-специфичный код, например, поддерживает манипуляции с GDT (global descriptor tables) и LDT (local descriptor tables) — аппаратными средствами x86. Другим примером архитектурно-специфичного кода ядра может служить интерфейс, предоставляющий поддержку буфера трансляции и процессорного кэша. Эта поддержка требует разного кода на разных архитектурах, поскольку кэш в них реализуется различным образом. Еще один пример — переключение контекста. Хотя на высоком уровне для выбора потоков и переключения контекста применяется один и тот же алгоритм (сохраняется контекст предыдущего потока, загружается контекст нового и запускается новый поток), существуют архитектурные различия между его реализациями для разных процессоров. Поскольку контекст описывается состоянием процессора (его регистров и т. д.), сохраняемая и загружаемая информация зависит от архитектуры. Как отмечалось в начале этой главы, одной из важнейших особенностей архитектуры Windows является переносимость между различными аппаратными платформами. Ключевой компонент, обеспечивающий такую переносимость, — уровень абстрагирования от оборудования (hardware abstraction layer, HAL). HAL — это загружаемый модуль режима ядра (Hal.dll), предоставляющий низкоуровневый интерфейс с аппаратной платформой, на которой выполняется Windows. Он скрывает от операционной системы специфику конкретной аппаратной платформы, в том числе ее интерфейсов ввода-вывода, контроллеров прерываний и механизмов взаимодействия между процессорами, т. е. все функции, зависимые от архитектуры и от конкретной машины. Когда внутренним компонентам Windows и драйверам устройств нужна платформенно-зависимая информация, они обращаются не к самому оборудованию, а к подпрограммам HAL, что и обеспечивает переносимость этой операционной системы. По этой причине подпрограммы HAL документированы в Windows DDK, где вы найдете более подробные сведения о HAL и о его использовании драйверами. Хотя в Windows имеется несколько модулей HAL (см. таблицу 2–6), при установке на жесткий диск компьютера копируется только один из них — Hal.dll. (B других операционных системах, например в VMS, нужный модуль HAL выбирается при загрузке системы.) Поскольку для поддержки разных процессоров требуются разные модули HAL, системный диск от одной х86-установки скорее всего не подойдет для загрузки системы с другим процессором. Таблица 2–6. ЭКСПЕРИМЕНТ: просмотр базовых HAL, включенных в Windows Для просмотра HAL, включенных в Windows, откройте файл Driver.cab в соответствующем подкаталоге, специфичном для конкретной архитектуры, в каталоге \Windows\Driver Cache. (Например, для систем x86 имя этого файла — \Windows\Driver Cache\i386\Driver.cab.) Прокрутите список до файлов, начинающихся с «Hal», и вы увидите файлы, перечисленные в таблице 2–6. ЭКСПЕРИМЕНТ: определяем используемый модуль HAL Определить, какой модуль HAL используется на вашей машине, можно двумя способами. 1. Откройте файл \Windows\Repair\Setup.log, найдите строку с 2. Откройте Device Manager (Диспетчер устройств): щелкните правой кнопкой мыши значок My Computer (Мой компьютер) на рабочем столе, выберите команду Properties (Свойства), откройте вкладку Hardware (Оборудование) и щелкните кнопку Device Manager (Диспетчер устройств). Проверьте имя «драйвера» для устройства Computer (Компьютер). ЭКСПЕРИМЕНТ: просмотр зависимостей NTOSKRNL и HAL Вы можете просмотреть взаимосвязи образов ядра и HAL, изучив их таблицы импорта и экспорта с помощью утилиты Dependency Walker (Depends.exe), которая содержится в Windows Support Tools и Platform SDK. Для исследования файла в Dependency Walker откройте его командой Open из меню File. Вот пример вывода этой утилиты при просмотре зависимостей в Ntoskrnl. Обратите внимание, что Ntoskrnl связан с HAL, который в свою очередь связан с Ntoskrnl (оба используют функции друг у друга). Ntoskrnl также связан с Bootvid.dll, видеодрайвером, используемым для вывода заставки при запуске Windows. B Windows XP и выше вы увидите в списке дополнительную DLL, Kdcom.dll. Она содержит код инфраструктуры отладчика ядра, который раньше был частью Ntoskrnl.exe. Подробное описание информации, выводимой Dependency Walker, см. в справочном файле этой утилиты (Depends.hlp). Драйверы устройств подробно описываются в главе 9, а здесь мы даем краткий обзор их типов и поясняем, как перечислить установленные драйверы, загруженные в системе. Драйверы устройств являются загружаемыми модулями режима ядра (как правило, это файлы с расширением. sys); они образуют интерфейс между диспетчером ввода-вывода и соответствующим оборудованием. Эти драйверы выполняются в режиме ядра в одном из трех контекстов: • в контексте пользовательского потока, инициировавшего функцию ввода-вывода; • в контексте системного потока режима ядра; • как результат прерывания (а значит, не в контексте какого-либо процесса или потока, который был текущим на момент прерывания). Как было сказано в предыдущем разделе, в Windows драйверы устройств не управляют оборудованием напрямую — вместо этого они вызывают функции HAL. Драйверы, как правило, пишутся на C (иногда на C++), поэтому при правильном использовании процедур HAL они являются переносимыми между поддерживаемыми Windows архитектурами на уровне исходного кода, а на уровне двоичных файлов — внутри семейства с одинаковой архитектурой. Существует несколько типов драйверов устройств. • • • • • • Поскольку установка драйвера устройства — единственный способ добавления в систему стороннего кода режима ядра, некоторые программисты пишут драйверы просто для того, чтобы получить доступ к внутренним функциям или структурам данных операционной системы, недоступным из пользовательского режима (но документированным и поддерживаемым в DDK). Например, многие утилиты с B Windows 2000 была введена поддержка Plug and Play и энергосберегающих технологий, а также расширена модель драйверов Windows NT, называемая Windows Driver Model (WDM). Windows 2000 и более поздние версии могут работать с унаследованными драйверами Windows NT 4, но, поскольку они не поддерживают Plug and Play и энергосберегающие технологии, функциональность системы в этом случае будет ограничена. C точки зрения WDM, существует три типа драйверов. • • • B среде WDM один драйвер не может контролировать все аспекты устройства: драйвер шины информирует диспетчер PnP об устройствах, подключенных к шине, в то время как функциональный драйвер управляет устройством. B большинстве случаев драйвер фильтра более низкого уровня модифицирует поведение устройства. Например, если устройство сообщает драйверу своей шины о том, что ему нужно 4 порта ввода-вывода, тогда как на самом деле ему требуется 16, драйвер фильтра может перехватить список аппаратных ресурсов, направляемый драйвером шины диспетчеру PnP и исправить число портов. Драйвер фильтра более высокого уровня обычно придает устройству дополнительную функциональность. Так, высокоуровневый драйвер фильтра для клавиатуры может обеспечивать дополнительную защиту. Обработка прерываний описывается в главе 3. Подробнее о диспетчере ввода-вывода, WDM, Plug and Play и энергосберегающих технологиях см. главу 9. ЭКСПЕРИМЕНТ: просмотр установленных драйверов устройств Чтобы вывести список установленных драйверов, запустите оснастку Computer Management (Управление компьютером). Для этого выберите из меню Start (Пуск) команду Programs (Программы), затем Administrative Tools (Администрирование) и Computer Management (Управление компьютером) или откройте Control Panel (Панель управления) и дважды щелкните значок Computer Management. B окне Computer Management раскройте System Information (Сведения о системе), затем Software Environment (Программная среда) и Drivers (Драйверы). Ниже приведен пример списка драйверов. B этом окне выводится список драйверов, определенных в реестре, а также их тип и состояние — Running (Работает) или Stopped (Остановлена). Драйверы устройств и процессы Windows-сервисов определяются в разделе реестра HKLM\SYSTEM\CurrentControlSet\Services. Однако они отличаются по коду типа, например 1 соответствует драйверу режима ядра. (Полный список хранящихся в реестре сведений, которые относятся к драйверам, см. в таблице 4–7.) Список загруженных в текущий момент драйверов можно просмотреть и с помощью утилиты Drivers (Drivers.exe в ресурсах Windows 2000) или Pstat (Pstat.exe в Windows XP Support Tools, Windows Server 2003 Support Tools, ресурсах Windows 2000 и Platform SDK). Ниже приведен листинг части выходной информации утилиты Drivers. Утилита перечисляет все загруженные компоненты режима ядра (Ntoskrnl, HAL и драйверы устройств) и сообщает размеры разделов в каждом образе. Pstat также выводит список загруженных драйверов, но только после списка процессов и потоков в каждом процессе. Она показывает один вид очень важной информации, не сообщаемой утилитой Drivers: адрес загрузки модуля в системном пространстве. Как вы еще увидите, этот адрес нужен для увязки выполняемых системных потоков с драйвером, в котором они существуют. Просмотр имен экспортируемых или глобальных символов в ключевых системных файлах (Ntoskrnl.exe, Hal.dll или Ntdll.dll) может оказаться полезным: вы получите представление о том, что умеет делать Windows в сравнении с документированной и поддерживаемой частью. Конечно, знание имен этих функций еще не означает, что вы сможете или должны их вызывать. Эти интерфейсы не документированы и могут быть изменены. Мы предлагаем рассмотреть эти функции только для лучшего понимания внутренних функций Windows, а не для обхода поддерживаемых интерфейсов. Например, просмотрев список функций в Ntdll.dll, вы сможете сравнить список всех системных сервисов, которые Windows предоставляет DLL-модулям подсистем пользовательского режима, с их подмножеством, предоставляемым каждой подсистемой. Хотя многие из этих функций точно соответствуют документированным и поддерживаемым Windows-функциям, некоторые из них недоступны через Windows API (см. статью «Inside the Native API» на C другой стороны, было бы интересно выяснить, что импортируют DLL-модули подсистемы Windows (скажем, Kernel32.dll или Adva-pi32.dll) и какие функции они вызывают в Ntdll. Также представляет интерес содержимое Ntoskrnl.exe: хотя в Windows DDK документированы многие экспортируемые подпрограммы, используемые драйверами режима ядра, немалая их часть не описана. Возможно, вас заинтересует содержимое таблицы импорта для Ntoskrnl и HAL, где перечислены функции HAL, используемые Ntoskrnl, и наоборот. B таблице 2–7 приведено большинство общеупотребительных префиксов имен функций в компонентах исполнительной системы. B каждом из таких компонентов также используются слегка модифицированные префиксы, обозначающие внутренние функции: либо за первой буквой префикса указывается / (от Понимая схему именования системных процедур Windows, расшифровывать имена экспортируемых функций гораздо легче. Общий формат выглядит так: где Например, B каждой системе Windows выполняются перечисленные ниже процессы. (Два из них, IdIe и System, не являются процессами в строгом смысле этого слова, поскольку они не выполняют какой-либо код пользовательского режима.) • Процесс IdIe (включает по одному потоку на процессор для учета времени простоя процессора). • Процесс System (содержит большинство системных потоков режима ядра). • Диспетчер сеансов (Smss.exe). • Подсистема Windows (Csrss.exe). • Процесс входа в систему (Winlogon.exe). • Диспетчер управления сервисами (Services.exe) и создаваемые им дочерние процессы сервисов (например, универсальный процесс для хостинга сервисов, Svchost.exe). • Серверный процесс локальной аутентификации (Lsass.exe). Чтобы понять взаимоотношения этих процессов, полезно просмотреть «дерево» процессов, отражающее связи между родительскими и дочерними процессами. Увидев, кем создается тот или иной процесс, вам будет легче понять, откуда берется каждый процесс. Ha рис. 2–6 показана часть экранного снимка дерева процессов с комментариями по нескольким первым процессам. (Process Explorer позволяет добавлять комментарии для индивидуальных процессов и выводить их как дополнительную колонку в окне.) B следующих разделах поясняются основные системные процессы, перечисленные на рис. 2–6. Хотя в этих разделах дается краткое описание последовательности запуска данных процессов, подробно все этапы загрузки Windows рассматриваются в главе 5. Первый процесс, показанный на рис. 2–6, является процессом простоя системы (system idle process). Как будет показано в главе 6, процессы идентифицируются по именам их образов. Однако этот процесс (как и процесс System) не выполняет реальный код пользовательского режима (в том смысле, что в каталоге \Windows нет «System IdIe Process.exe»). Кроме того, разные утилиты из-за особенностей реализации по-разному именуют его. B таблице 2–8 приводится несколько имен процесса IdIe (с идентификатором 0); подробнее о нем рассказывается в главе 6. A теперь рассмотрим системные потоки и предназначение каждого системного процесса, выполняющего реальный код. Две строки, помеченные как Interrupts и DPCs, отражают время, затраченное на обслуживание прерываний и обработку отложенных вызовов процедур (deferred procedure calls, DPC). Эти механизмы объясняются в главе 3. Заметьте: хотя Process Explorer показывает эти строки в списке процессов, они не имеют отношения к процессам. Они выводятся потому, что ведут учет процессорного времени, не выделенного какому-либо процессу. Ho Task Manager (Диспетчер задач) рассматривает время, затраченное на обработку преры- ваний и DPC, как время простоя системы. Поэтому система, занятая интенсивной обработкой прерываний, будет выглядеть в Task Manager так, будто она ничем не занимается. Процесс System (с идентификатором 8 в Windows 2000 и идентификатором 4 в Windows XP и Windows Server 2003) служит носителем особых потоков, работающих только в режиме ядра, — Системные потоки создаются функцией По умолчанию владельцем системных потоков является процесс System, но драйверы могут создавать системные потоки в любом процессе. Например, драйвер подсистемы Windows (Win32k.sys) создает системные потоки в процессе подсистемы Windows (Csrss.exe), чтобы облегчить доступ к данным в адресном пространстве этого процесса в пользовательском режиме. Если вы занимаетесь поиском причин неполадок или системным анализом, полезно сопоставить выполнение индивидуальных системных потоков с создавшими их драйверами или даже с подпрограммой, содержащей соответствующий код. Так, на сильно загруженном файл-сервере процесс System скорее всего потребляет значительную часть процессорного времени. Ho для определения того, какой именно драйвер или компонент операционной системы выполняется, просто знать, что процесс System в данный момент выполняет «какой-то системный поток», недостаточно. Так что, если в процессе System выполняются потоки, сначала определите, какие это потоки (например, с помощью оснастки Performance). Найдя такой поток (или потоки), посмотрите, в каком драйвере началось выполнение системного потока (это по крайней мере укажет на наиболее вероятного создателя потока), либо проанализируйте стек вызовов (или хотя бы текущий адрес) интересующего вас потока, что позволит определить, в каком месте он сейчас выполняется. Оба этих метода демонстрируются следующими экспериментами. ЭКСПЕРИМЕНТ: идентификация системных потоков в процессе System Вы можете убедиться, что потоки внутри процесса System должны быть потоками режима ядра, поскольку стартовый адрес каждого из них больше адреса начала системного пространства (которое по умолчанию начинается с 0x80000000, если система загружена без параметра /3GB в Boot.ini). Кроме того, обратив внимание на процессорное время, выделяемое этим потокам, вы увидите, что они занимают процессорное время только при выполнении в режиме ядра. Чтобы определить драйвер, создавший системный поток, найдите стартовый адрес потока (с помощью Pviewer.exe) и ищите драйвер с базовым адресом, ближайшим (с меньшей стороны) к этому стартовому адресу. Утилита Pstat в конце своих выходных данных, как и команда Чтобы быстро найти текущий адрес потока, воспользуйтесь командой B первом столбце выводятся идентификаторы процесса и потока (в виде «Идентификатор процесса. Идентификатор потока»). Bo втором сообщается текущий адрес потока, в третьем — состояние потока: ожидает, готов или выполняется (о состояниях потоков см. главу 6). B последнем столбце показывается адрес вершины стека потока. Эта информация помогает определить драйвер, в котором началось выполнение того или иного потока. Имя функции потока в Ntoskrnl дает дополнительную подсказку о том, что именно делает поток. Однако, если выполняемый поток является одним из рабочих потоков системы (ExpWorkerThread), вы не сможете точно сказать, что он делает, поскольку любой драйвер может давать задания рабочему потоку системы. B этом случае единственный выход — поставить точку прерывания в ЭКСПЕРИМЕНТ: увязка системного потока с драйвером устройства B этом эксперименте мы посмотрим, как увязать активность процессора в процессе System с системным потоком (и драйвером, к которому он относится), вызывающим эту активность. Это важно: чтобы по-настоящему понять, что происходит, нужно перейти на уровень потоков процесса System. B данном случае мы вызовем активность системного потока, создав нагрузку файлового сервера на компьютере. (Драйвер файл-сервера Srv.sys создает системные потоки для обработки входящих запросов на файловый ввод-вывод. Подробнее об этом компоненте см. главу 13.) 1. Откройте окно командной строки. 2. Создайте список всех каталогов на диске С, используя сетевой путь для доступа к этому диску. Например, если имя вашего компьютера — COMPUTERl, введите dir \\computerl\c$ /s. (Ключ 3. Запустите Process Explorer и дважды щелкните процесс System. 4. Откройте вкладку Threads. 5. Отсортируйте список по столбцу CSwitch Delta (разница по числу переключений контекста). Вы должны увидеть один или более потоков в Srv.sys, как показано на следующей иллюстрации. Если вы видите работающий системный поток и не уверены, какой это драйвер, нажмите кнопку Module, которая открывает окно свойств для файла. Например, нажатие кнопки Module при выбранном, как на предыдущей иллюстрации, потоке в Srv.sys выводит результаты в следующем окне. Диспетчер сеансов (Session Manager) (\Windows\System32\Smss.exe) является первым процессом пользовательского режима, создаваемым в системе. Он порождается системным потоком режима ядра, отвечающим за последний этап инициализации исполнительной системы и ядра. Диспетчер сеансов отвечает за некоторые важные этапы запуска Windows, такие как создание дополнительных страничных файлов, выполнение отложенных операций по копированию, переименованию и удалению файлов, а также создание системных переменных окружения. Он также запускает процессы подсистем (обычно только Csrss.exe) и Winlogon, который в свою очередь создает остальные системные процессы. Значительная часть сведений о конфигурации, хранящихся в реестре и используемых при инициализации Smss, находится в HKLM\SYSTEM\Current-ControlSet\Control\Session Manager. Некоторые из этих данных поясняются в главе 5 в разделе по Smss. (Более подробное описание разделов и параметров см. в справочном файле Regentry.chm из ресурсов Windows 2000). После выполнения этих этапов инициализации главный поток Smss переходит к бесконечному ожиданию описателей процессов Csrss и Winlogon. Так как от них зависит функционирование Windows, при неожиданном завершении любого из них Smss вызывает крах системы (с кодом STATUSSYSTEM_ PROCESS_TERMINATED, или 0xC000021A). Smss также ожидает запросы на загрузку, события отладки и запросы на запуск новых сеансов сервера терминала. (Описание служб терминала см. в главе 1.) Сеанс Terminal Services создается Smss. Когда Smss получает запрос на создание сеанса, он сначала вызывает Процесс входа BWindows (\Windows\System32\Winlogon.exe) обрабатывает интерактивный вход пользователя в систему и выход из нее. При нажатии комбинации клавиш SAS (secure attention sequence) Winlogon получает уведомление о запросе пользователя на вход в систему. По умолчанию SAS в Windows представляет собой комбинацию клавиш Ctrl+Alt+Del. Назначение SAS — защита пользователя от программ перехвата паролей, имитирующих процесс входа в систему, так как эту комбинацию клавиш нельзя перехватить в приложении пользовательского режима. Идентификация и аутентификация при входе в систему реализованы в заменяемой DLL под названием GINA (Graphical Identification and Authentication). Стандартная GINA Windows, Msgina.dll, реализует интерфейс для входа в систему по умолчанию. Однако разработчики могут включать свои GINA DLL, реализующие другие механизмы аутентификации и идентификации вместо стандартного метода Windows на основе проверки имени и пароля пользователя — например, на основе распознавания образцов голоса. Кроме того, Winlogon может загружать дополнительные DLL компонентов сетевого доступа для дальнейшей аутентификации. Эта функция позволяет нескольким компонентам доступа к сетям единовременно собирать все необходимые регистрационные данные в процессе обычного входа в систему. После ввода имя и пароль пользователя посылаются для проверки серверному процессу локальной аутентификации (local security authentication server process, LSASS) (\Windows\System32\Lsass.exe, описываемому в главе 8). LSASS вызывает соответствующую функциональность (реализованную в виде DLL) для проверки соответствия введенного пароля с тем, что хранится в активном каталоге или SAM (части реестра, содержащей определения пользователей и групп). После успешной аутентификации LSASS вызывает какую-либо функцию в мониторе состояния защиты (например Userinit выполняет некоторые действия по инициализации пользовательской среды (например, запускает сценарии регистрации и активизирует групповые политики), а затем ищет в реестре параметр Shell (в указанном выше разделе Winlogon) и создает процесс для запуска определенной системной оболочки (по умолчанию — Explorer.exe). После этого процесс Userinit завершается. Вот почему для Explorer.exe не показывается родительский процесс — он уже завершился. Иначе говоря, Explorer является «внучатым» процессом Winlogon. (Имена процессов, чьи родительские процессы уже завершены, в списке Tlist выравниваются по левому краю.) Winlogon активен не только при входе и выходе пользователя, но и при перехвате ввода SAS с клавиатуры. Например, когда вы нажимаете Ctrl+Alt+ DeI после входа в систему, Winlogon открывает диалоговое окно Windows Security (Безопасность Windows), предлагающее на выбор выход из системы, запуск Task Manager, блокировку рабочей станции, завершение работы системы и т. д. Полное описание этапов процесса входа см. в разделе «Smss, Csrss и Winlogon» главы 5. Подробнее об аутентификации см. главу 8. Подробнее о вызываемых функциях интерфейса LSASS (чьи имена начинаются с Вспомните, что термин «сервисы» в Windows обозначает как серверные процессы, так и драйверы устройств. B этом разделе обсуждаются сервисы, являющиеся процессами пользовательского режима. Они похожи на демоны UNIX или обособленные процессы VMS в том смысле, что могут быть сконфигурированы на автоматический запуск при загрузке системы, не требуя интерактивного входа. Их также можно запустить вручную, например, с помощью оснастки Services (Службы) или вызовом Windows-функции Этими сервисами управляет специальный системный процесс, диспетчер управления сервисами (service control manager) (\Windows\System32\Servi-ces.exe), отвечающий за запуск, остановку процессов сервисов и взаимодействие с ними. Сервисы представляют собой просто Windows-образы исполняемых программ, вызывающие особые Windows-функции для взаимодействия с диспетчером управления сервисами и с его помощью выполняющие такие операции, как регистрация успешного запуска сервиса, ответы на запросы о состоянии, приостановку или завершение работы сервиса. Сервисы определяются в разделе реестра HKLM\SYSTEM\CurrentControlSet\Services. Сведения о подразделах и параметрах, относящихся к сервисам, см. в справочном файле Regentry.chm в ресурсах Windows. Учтите, что у сервисов есть три имени: имя процесса, выполняемого в системе, внутреннее имя в реестре и так называемое отображаемое имя (display name), которое можно увидеть в оснастке Services (Службы). (He у каждого сервиса есть отображаемое имя, и в случае его отсутствия используется внутреннее имя.) Сервисы Windows также содержат поле описания, где находится более подробная информация о том, что делает конкретный сервис. Чтобы выяснить, какие именно сервисы содержатся в том или ином процессе, введите команду tlist /s. Ho заметьте, что иногда один процесс совместно используется несколькими сервисами. Код типа в реестре позволяет узнать, какие сервисы имеют собственные процессы и какие из них делят процессы с другими сервисами данного образа файла. B виде сервисов реализуются некоторые компоненты Windows, например диспетчер очереди печати (спулер), журнал системных событий, планировщик задач, а также ряд сетевых компонентов. ЭКСПЕРИМЕНТ: вывод списка установленных сервисов Чтобы вывести список установленных сервисов (служб), дважды щелкните значок Administrative Tools (Администрирование) в окне Control Panel (Панель управления) и выберите Services (Службы). Вы должны увидеть что-нибудь в таком роде: Для просмотра детальных сведений о сервисе щелкните правой кнопкой мыши имя сервиса и выберите команду Properties (Свойства). Ниже показан пример окна свойств для службы Print Spooler (Диспетчер очереди печати). Обратите внимание, что поле Path To Executable (Исполняемый файл) указывает на программу, включающую данный сервис. Помните, что некоторые сервисы разделяют процессы с другими сервисами, поэтому число сервисов и используемых ими процессов не всегда находится в соотношении «один к одному». ЭКСПЕРИМЕНТ: просмотр сервисов внутри сервисных процессов Process Explorer выделяет процессы, которые являются хостами одного и более сервисов. (Для настройки поведения Process Explorer выберите Configure Highlighting в меню Options.) Дважды щелкнув процесс — хост сервисов, вы откроете вкладку Services, где перечисляются сервисы внутри этого процесса. При этом по каждому сервису выводится имя раздела реестра, где определен данный сервис, отображаемое имя, видимое администратору, и текст описания для этого сервиса (если такой текст есть). Например, в Windows XP список сервисов в процессе Svchost.exe, выполняемом под учетной записью System, выглядит следующим образом. Подробнее о сервисах рассказывается в главе 4. B этой главе мы познакомились с общими аспектами системной архитектуры Windows. Мы также рассмотрели ключевые компоненты Windows и принципы их взаимодействия. B следующей главе будет подробнее рассказано о базовых системных механизмах, на которые опираются эти компоненты, в том числе о синхронизации и диспетчере объектов. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|