1.Внутреннее устройство Windows (гл. 1-4) - Марк Руссинович
Шрифт:
Интервал:
Закладка:
ЭКСПЕРИМЕНТ: просмотр таблицы описателей с помощью отладчика ядра
Команда !bandle отладчика ядра допускает следующие аргументы:
!handle ‹индекс_описателя› ‹флаги› ‹идентификатор_процесса›
Индекс описателя определяет элемент в таблице описателей (0 — вывод всех описателей). Индекс первого описателя равен 4, второго — 8 и т. д. Например, введя !bandle 4, вы увидите первый описатель в текущем процессе.
Вы можете указывать флаги, являющиеся битовыми масками, где бит 0 означает, что нужно вывести лишь информацию из элемента таблицы, бит 1 — показать не только используемые, но и свободные описатели, а бит 2 — сообщить информацию об объекте, на который ссылается описатель. Следующая команда выводит полную информацию о таблице описателей в процессе с идентификатором 0x408.
Защита объектовОткрывая файл, нужно указать, для чего это делается — для чтения или записи. Если вы попытаетесь записать что-нибудь в файл, открытый для чтения, то получите ошибку. Аналогичным образом действует и исполнительная система: когда процесс создает объект или открывает описатель существующего объекта, он должен указывать набор желательных прав доступа (desired access rights), сообщая тем самым, что именно он собирается делать с объектом. Процесс может запросить либо набор стандартных прав доступа (чтение, запись, выполнение), применимых ко всем объектам, либо специфические права доступа, различные для объектов разного типа. Так, в случае объекта «файл» процесс может запросить права на удаление файла или дозапись, а в случае объекта «поток» — права на приостановку потока или его завершение.
Когда процесс открывает описатель объекта, диспетчер объектов вызывает так называемый монитор состояния защиты* (security reference monitor), часть подсистемы защиты, работающую в режиме ядра, и посылает ему уведомление о наборе желательных для процесса прав доступа. Монитор состояния защиты проверяет, разрешает ли дескриптор защиты объекта запрашиваемый тип доступа. Если да, монитор состояния защиты возвращает процессу набор предоставленных прав доступа (granted access rights), информацию о которых диспетчер объектов сохраняет в создаваемом им описателе объекта. Как подсистема защиты определяет, кто и к каким объектам может получать доступ, рассматривается в главе 8.
* Ha самом деле этот компонент представляет собой нечто вроде монитора запросов к подсистеме защиты. — Прим. перев.
После этого всякий раз, когда потоки процесса используют описатель, диспетчер объектов может быстро проверить, соответствует ли набор предоставленных прав доступа, хранящихся в описателе, действиям, которые намеревается выполнить вызванный потоками сервис объекта. Так, если вызывающая программа запросила доступ для чтения к объекту «раздел», а затем вызывает сервис для записи в этот объект, последний сервис не выполняется.
Хранение объектов в памятиОбъекты бывают двух типов: временные (temporary) и постоянные (permanent). Большинство объектов временные, т. е. они хранятся, пока используются, и освобождаются, как только необходимость в них отпадает. Постоянные объекты существуют до тех пор, пока они не освобождаются явным образом. Поскольку большинство объектов временные, остальная часть этого раздела будет посвящена тому, как диспетчер объектов реализует хранение объектов в памяти (object retention), т. е. сохранение временных объектов лишь до тех пор, пока они используются, с их последующим удалением. Так как для доступа к объекту все процессы пользовательского режима должны сначала открыть его описатель, диспетчер объектов может легко отслеживать, сколько процессов и даже какие именно из них используют объект. Учет описателей является одним из механизмов, реализующих хранение объектов в памяти. Этот механизм двухфазный. Первая фаза называется хранением имен (name retention) и контролируется числом открытых описателей объекта. Каждый раз, когда процесс открывает описатель объекта, диспетчер увеличивает значение счетчика открытых описателей в заголовке объекта. По мере того как процессы завершают использование объекта и закрывают его описатели, диспетчер уменьшает значение этого счетчика. Когда счетчик обнуляется, диспетчер удаляет имя объекта из своего глобального пространства имен. После этого новые процессы уже не смогут открывать описатели данного объекта.
Вторая фаза заключается в том, что прекращается хранение тех объектов, которые больше не используются (т. е. они удаляются). Так как код операционной системы обычно обращается к объектам по указателям, а не описателям, диспетчер объектов должен регистрировать и число указателей объектов, переданных процессам операционной системы. При каждой выдаче указателя на объект он увеличивает значение счетчика ссылок на объект. Компоненты режима ядра, прекратив использовать указатель, вызывают диспетчер объектов для уменьшения счетчика ссылок. Система также увеличивает счетчик ссылок при увеличении счетчика описателей, а при уменьшении счетчика описателей соответственно уменьшает счетчик ссылок, поскольку описатель тоже является подлежащей учету ссылкой на объект (подробнее об этих механизмах см. описание функции ObReferenceObjectByPointer или ObDere-ferenceObject в DDK).
Ha рис. 3-22 показаны два задействованных объекта-события. Процесс A открыл первый объект, а процесс B — оба объекта. Кроме того, на первый объект ссылается какая-то структура режима ядра, и его счетчик ссылок paвен 3. Так что, даже если оба процесса закроют свои описатели первого объекта, он по-прежнему будет существовать, поскольку его счетчик ссылок еще не обнулится. Ho, когда процесс B закроет свой описатель второго объекта, этот объект будет удален.
Таким образом, даже если счетчик открытых описателей объекта обну-лится, счетчик ссылок может превышать нулевое значение, указывая, что операционная система еще использует объект. B конце концов счетчик ссылок тоже обнулится, и тогда диспетчер удалит соответствующий объект из памяти.
Такой механизм позволяет хранить объект и его имя в памяти, просто не закрывая его описатель. Программистам, создающим приложения с двумя и более взаимодействующими процессами, не приходится беспокоиться о том, что один из процессов удалит объект в то время, когда он еще используется другим процессом. Кроме того, закрытие описателей объекта, принадлежащих приложению, еще не означает, что этот объект будет немедленно удален, — он может использоваться операционной системой. Например, какой-то процесс создает второй процесс для выполнения программы в фоновом режиме, после чего немедленно закрывает описатель созданного процесса. Так как второй процесс еще не закончил свою работу и выполняется операционной системой, она поддерживает ссылку на этот объект «процесс». Диспетчер сможет обнулить счетчик ссылок на второй процесс и удалить его, только когда завершится выполнение этого процесса.
Учет ресурсовУчет ресурсов, как и хранение объектов, тесно связан с использованием описателей объектов. Положительное значение счетчика открытых описателей указывает на то, что данный ресурс задействован какими-то процессами. Когда счетчик описателей и счетчик ссылок на некий объект обнуляются, процессы, использовавшие этот объект, больше не занимают память, отведенную под него.
Bo многих операционных системах для ограничения доступа процессов к системным ресурсам применяется система квот. Однако типы устанавливаемых для процессов квот иногда весьма разнообразны, а отслеживающий квоты код распределен по всей операционной системе. Так, в некоторых операционных системах компонент ввода-вывода может регистрировать и ограничивать число файлов, которые может открыть процесс, а компонент управления памятью может накладывать ограничения на объем памяти, выделяемой потокам процесса. Компонент, отвечающий за управление процессами, способен ограничивать максимальное число новых процессов или новых потоков процесса. Каждое из этих ограничений отслеживается и реализуется в различных частях операционной системы.
Диспетчер объектов Windows, напротив, представляет собой компонент централизованного учета ресурсов. B заголовке каждого объекта содержится атрибут квоты, определяющий, насколько диспетчер объектов уменьшает квоту подкачиваемой или неподкачиваемой памяти процесса при открытии его потоком описателя этого объекта.
У каждого процесса в Windows имеется структура квот, регистрирующая лимиты и текущее количество используемой памяти из подкачиваемого и неподкачиваемого пулов, а также из страничного файла. (Введите dt nt!_ EPROCESS_QUOTA_ENTRY в отладчике ядра, чтобы увидеть формат этой структуры.) Значения данных квот по умолчанию равны 0 (ограничений нет), но их можно указать, модифицировав параметры в реестре (см. параметры NonPagedPoolQuota, PagedPoolQuota и PagingFileQuota в разделе HKLMSystemCurrentControlSetSession ManagerMemory Management). Заметьте, что все процессы в интерактивном сеансе используют один и тот же блок квот (документированного способа создания процессов с собственными блоками квот нет).