1.Внутреннее устройство Windows (гл. 1-4) - Марк Руссинович
Шрифт:
Интервал:
Закладка:
Использование объекта прерывания дает и другие преимущества: ядро может синхронизировать выполнение ISR с другими частями драйвера устройства, которые могут разделять данные с ISR. (Подробнее о том, как драйверы устройств реагируют на прерывания, см. главу 9.)
Более того, объекты прерывания позволяют ядру легко вызывать более одной ISR для любого уровня прерывания. Если несколько драйверов создают объекты прерывания и сопоставляют их с одной записью в IDT, то при прерывании на определенной линии диспетчер вызывает каждую из этих процедур (ISR). Такая функциональность позволяет ядру поддерживать конфигурации в виде цепочек, когда несколько устройств совместно используют одну линию прерывания. Когда одна из ISR объявляет диспетчеру о захвате прерывания, происходит разрыв цепочки. Если несколько устройств, разделяющих одну линию прерывания, одновременно запрашивают обслуживание, то устройства, не получившие подтверждения от своих ISR, будут вновь генерировать прерывания, как только диспетчер понизит IRQL. Связывание устройств в цепочку разрешается, только если все драйверы устройств, стремящиеся использовать одно и то же прерывание, сообщат ядру о своей способности разделять данное прерывание. Если они не в состоянии совместно использовать это прерывание, диспетчер Plug and Play переназначит IRQ с учетом запросов каждого устройства. Если разделяемым является вектор прерываний, объект прерывания вызывает KiChainedDispatch, которая поочередно обращается к ISR каждого зарегистрированного объекта прерывания, пока один из них не сообщит, что прерывание вызвано им, или пока все они не будут выполнены. B одном из предыдущих примеров вывода !idt вектор 0x3b был подключен к нескольким объектам прерываний, связанным в цепочку (chained interrupt objects).
Программные прерыванияХотя большинство прерываний генерируется аппаратно, ядро Windows тоже может генерировать прерывания — только они являются программными. Этот вид прерываний служит для решения многих задач, в том числе:
• инициации диспетчеризации потоков;
• обработки прерываний, не критичных по времени;
• обработки событий таймеров;
• асинхронного выполнения какой-либо процедуры в контексте конкретного потока;
• поддержки асинхронного ввода-вывода. Эти задачи подробно рассматриваются ниже.
Прерывания DPC или диспетчеризацииКогда дальнейшее выполнение потока невозможно, например из-за его завершения или перехода в ждущее состояние, ядро напрямую обращается к диспетчеру, чтобы вызвать немедленное переключение контекста. Однако иногда ядро обнаруживает, что перераспределение процессорного времени (rescheduling) должно произойти при выполнении глубоко вложенных уровней кода. B этой ситуации ядро запрашивает диспетчеризацию, но саму операцию откладывает до выполнения текущих действий. Такую задержку удобно организовать с помощью программного прерывания DPC (deferred procedure call).
При необходимости синхронизации доступа к разделяемым структурам ядра последнее всегда повышает IRQL процессора до уровня «DPC/dispatch» или выше. При этом дополнительные программные прерывания и диспетчеризация потоков запрещаются. Обнаружив необходимость в диспетчеризации, ядро генерирует прерывание уровня «DPC/dispatch». Ho поскольку IRQL уже находится на этом уровне или выше, процессор откладывает обработку этого прерывания. Когда ядро завершает свои операции, оно определяет, что должно последовать снижение IRQL ниже уровня "DPC/dispatch", и проверяет, не ожидают ли выполнения отложенные прерывания диспетчеризации. Если да, IRQL понижается до уровня «DPC/dispatch», и эти отложенные прерывания обрабатываются. Активизация диспетчера потоков через программное прерывание — способ отложить диспетчеризацию до подходящего момента. Однако Windows использует программные прерывания для отложенного выполнения и других операций.
При этом IRQL ядро обрабатывает не только диспетчеризацию потоков, но и DPC DPC — это функция, выполняющая системную задачу, менее критичную по времени в сравнении с текущей. Эти функции называются отложенными (deferred), так как не требуют немедленного выполнения.
DPC позволяют операционной системе генерировать прерывания и выполнять системные функции в режиме ядра. Ядро использует DPC для обработки прерываний по таймеру (и освобождения потоков, ждущих на таймерах), а также для перераспределения процессорного времени по истечении кванта времени, отведенного текущему потоку. Драйверы устройств используют DPC для выполнения запросов ввода-вывода. Для своевременного обслуживания аппаратных прерываний Windows — во взаимодействии с драйверами устройств — пытается удерживать текущий IRQL ниже IRQL устройств. Один из способов достижения этой цели заключается в следующем. ISR должна выполнять минимум действий по обслуживанию своего устройства, сохранять переменные данные о состоянии прерывания и откладывать передачу данных или выполнение других не столь критичных по времени операций, как DPC, до снижения IRQL к уровню «DPC/dispatch» (подробнее о DPC и системе ввода-вывода см. главу 9).
DPC представляется DPC-объектом, управляющим объектом ядра, невидимым программам пользовательского режима, но видимым драйверам и системному коду. Наиболее важной частью информации DPC-объекта является адрес системной функции, которую ядро должно вызвать для обработки прерывания DPC DPC-процедуры, ожидающие выполнения, хранятся в управляемых ядром очередях (по одной на каждый процессор). Эти очереди называются очередями DPC Запрашивая DPC, системный код вызывает ядро для инициализации DPC-объекта и помещает его в очередь DPC
По умолчанию ядро помещает DPC-объекты в конец очереди DPC процессора, на котором был запрошен DPC (как правило, это процессор, на котором выполняется ISR). Однако драйвер устройства может изменить это, указав приоритет DPC (низкий, средний или высокий; по умолчанию — средний) или направив DPC конкретному процессору. DPC, направленный конкретному процессору, называется целевым DPC (targeted DPC). Если у DPC низкий или средний приоритет, ядро помещает DPC-объект в конец очереди, а если у DPC высокий приоритет, то — в начало.
Когда IRQL процессора вот-вот понизится с уровня «DPC/dispatch» или более высокого до уровня «APC» или «passive», ядро переходит к обработке всех DPC Windows оставляет IRQL на уровне «DPC/dispatch» и извлекает все DPC-объекты из очереди данного процессора (т. е. ядро опустошает очередь), поочередно вызывая каждую DPC-функцию. Ядро разрешает уменьшить IRQL ниже уровня «DPC/dispatch» для продолжения выполнения обычных потоков только после опустошения очереди. Схема обработки DPC показана на рис. 3–7.
Приоритеты DPC могут влиять на поведение системы и иным способом. Обычно ядро начинает опустошение очереди DPC с прерывания уровня «DPC/dispatch». Такое прерывание генерируется ядром, только если DPC направлен на процессор, на котором выполняется ISR, и DPC имеет средний или высокий приоритет. Если у DPC низкий приоритет, ядро генерирует прерывание, только если число незавершенных запросов DPC превышает пороговое значение или если число DPC, запрошенных на процессоре за установленный период, невелико. Если DPC направлен другому процессору (не тому, на котором выполняется ISR) и его приоритет высокий, ядро немедленно посылает ему диспетчерское IPI, сигнализируя целевому процессору о необходимости опустошения его очереди DPC Если приоритет DPC средний или низкий, для появления прерывания «DPC/dispatch» число DPC в очереди целевого процессора должно превышать пороговое значение. Системный поток простоя также опустошает очередь DPC процессора, на котором он выполняется. Хотя уровни приоритета и направление DPC являются довольно гибкими средствами, у драйверов устройств редко возникает необходимость в изменении заданного по умолчанию поведения своих DPC-объектов. B таблице 3–1 даются сведения о ситуациях, в которых начинается опустошение очереди DPC
Поскольку потоки пользовательского режима выполняются при низком IRQL, вероятность того, что DPC прервет выполнение обычного пользовательского потока, довольно велика. DPC-процедуры выполняются независимо от того, какой поток работает в настоящий момент. Это означает, что выполняемая DPC-процедура не в состоянии предугадать текущий размер спроецированного адресного пространства процесса. DPC-процедуры могут вызывать функции ядра, но не могут обращаться к системным сервисам, генерировать ошибки страницы, создавать или ждать объекты диспетчера. Однако они способны получать доступ к неподкачиваемым областям системной памяти, поскольку системное адресное пространство всегда спроецировано независимо от того, что представляет собой текущий процесс.
DPC используются не только драйверами, но и ядром. Ядро чаще всего применяет DPC для обработки ситуации, когда истекает выделенный квант времени. При каждом такте системного таймера генерируется прерывание с IRQL-уровнем «clock». Обработчик прерываний таймера (выполняемый при IRQL, равном «clock») обновляет системное время и уменьшает значение счетчика, отслеживающего время выполнения текущего потока. Когда значение счетчика обнуляется, квант времени, отведенный потоку, заканчивается, и ядру может понадобиться перераспределить процессорное время — эта задача имеет более низкий приоритет и должна выполняться при IRQL, равном «DPC/dispatch». Обработчик прерываний таймера ставит DPC в очередь, чтобы инициировать диспетчеризацию потоков, после чего завершает свою работу и понижает IRQL процессора. Поскольку приоритет прерываний DPC ниже, чем аппаратных, перед генерацией прерывания DPC сначала обрабатываются все аппаратные прерывания, возникающие до завершения обработки прерывания таймера.