Программирование драйверов Windows

       

Особенности механизма DPC


Как правило, работа с отложенными процедурными вызовами не является сложной, поскольку операционные системы Windows 2000/XP/Server 2003 предлагают большой набор системных вызовов, скрывающих большую часть деталей этого процесса. Тем не менее, особо следует выделить два наиболее обманчивых момента в работе с DPC.

Во-первых, Windows NT 5 устанавливает ограничение, состоящее в том, что один экземпляр DPC объекта может быть помещен в системную DPC очередь в определенный временной промежуток. Попытки поместить в очередь DPC объект, в точности совпадающий с уже там присутствующим, отвергаются. В результате, происходит только один вызов DPC процедуры, даже если драйвер ожидает выполнение двух вызовов. Это может произойти, если два прерывания были вызваны обслуживаемым устройством, а обработка первого отложенного процедурного вызова еще не начиналась. Первый экземпляр DPC объекта еще пребывает в очереди, в то время как драйвер уже приступил к обработке второго прерывания.

Конструкция драйвера должна предусматривать такой ход работы DPC механизма. Возможно, следует предусмотреть дополнительно счетчик DPC запросов, либо драйвер может реализовать собственную реализацию очереди запросов. В момент выполнения реальной отложенной процедуры можно проверять счетчик и собственную очередь запросов для того, чтобы определить, какую конкретно работу следует выполнять.

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


Если присмотреться к списку параметров вызовов IoInitializeDpcRequest

и IoRequestDpc (предназначенных для работы с DpcForIsr процедурами), то нетрудно заметить, что DPC объект "привязан" к объекту устройства. При размещении этого объекта в DPC очереди в момент работы ISR процедуры также указывается объект устройства. Этим и достигается определенность, вызов какой конкретно DPC процедуры "заказан" ISR процедурой (соотнесение по объекту устройства). Это же говорит о том, что драйвер, который создал несколько объектов устройств (достаточно редкий случай), может эксплуатировать и несколько процедур DpcForIsr &#8212 по одной для каждого объекта устройства.

Системный DPC механизм предотвращает возможность одновременной обработки DPC объектов из системной очереди, даже в условиях многопроцессорной конфигурации. Таким образом, если ресурсы используются совместно несколькими отложенными процедурами, то нет необходимости заботиться о синхронизации доступа к ним.

Выше было рассмотрено использование DPC процедур для завершения обработки прерываний, то есть DpcForIsr. Однако DPC процедуры можно использовать и в другом ключе, например, совместно с таймерами для организации ожидания. Для этого создастся объект DPC при помощи вызова KeInitializeDPC, который связывает этот объект с DPC процедурой, входящей в состав драйвера. После этого можно выполнять установку времени ожидания в предварительно инициализированном (используя KeInitializeTimer или KeInitializeEx) объекте таймера. Для установки интервала ожидания используется вызов KeSetTimer, которому в качестве одного из параметров необходимо передать указатель на инициализированный DPC объект. Пo истечении интервала ожидания DPC объект будет внесен в системную DPC очередь, и DPC процедура, ассоциированная с ним, будет вызвана так скоро, насколько этo будет возможно. Процедуры DPC данного, второго, типа обозначены в документации DDK термином 'Custom DPC'. (Этот вариант использования DPC процедур делает их весьма напоминающими APC вызовы пользовательского режима.)
Для размещения в системной DPC очереди объектов, соответствующих второму типу DPC процедур (не связанных с прерываниями), следует использовать вызов KeInsertQueueDpc. Соответственно, код-инициатор вызова должен работать на уровне IRQL не ниже DISPATCH_LEVEL.

Для очистки системной DPC очереди от Custom DPC процедур, например, если драйвер должен срочно завершить работу, предназначен вызов KeRemoveQueueDpc, который может быть вызван из кода любого уровня IRQL.


Содержание раздела