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

Системные рабочие потоки


Для нерегулярных коротких операций на уровне IRQL, равном PASSIVE_LEVEL, использование полноценных потоков, которые создаются и тут же завершаются, вряд ли будет эффективным. Альтернативой этому может быть создание системных рабочих потоков, system worker threads.

Для того чтобы проделать какую-нибудь несложную и не очень продолжительную работу, драйвер должен выделить память под структуру типа WORK_QUEUE_ITEM, затем инициализировать ее вызовом ExInitializeWorkItem, связав с ней собственную функцию (callback-функцию), и поместить ее в очередь объектов WORK_QUEUE_ITEM вызовом ExQueueWorkItem. Приоритет, на котором будет работать код вызываемой callback-функции, зависит от второго параметра вызова ExQueueWorkItem, QueueType, то есть от того, в какую очередь помещен данный объект WORK_QUEUE_ITEM, например, DelayedWorkQueue. В конце своей работы вызванная callback-функция должна освободить память, занятую под объектом типа WORK_QUEUE_ITEM (указатель на него поступает в callback-функцию при вызове).

Перечисленные функции ExInitializeWorkItem и ExQueueWorkItem

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

typedef struct _MY_WORK_ITEM { WORK_QUEUE_ITEM Item; char AdditionalData[64]; } MY_WORK_ITEM, *PMY_WORK_ITEM;

Данная структура создается и удаляется драйвером, что позволяет ей иметь нестандартную длину &#8212 главное, что начальный блок используется обычным для системы способом (как для WORK_QUEUE_ITEM).

Таблица 10.3. Прототип вызова IoAllocateWorkItem



PIO_WORKITEM IoAllocateWorkItem IRQL&#60=DISPATCH_LEVEL
Параметры Создает объект рабочего потока
IN PDEVICE_OBJECT pDevObject Объект устройства инициатора вызова
Возвращаемое значение Указатель на созданный объект или NULL в случае неудачи

Новые (поддерживаются в Windows Me/2000/XP/2003) предлагаемые вызовы IoAllocateWorkItem, IoQueueWorkItem и IoFreeWorkItem перераспределили обязанности.
Теперь вызов IoAllocateWorkItem (таблица 10.3) создает структуры типа IO_WORKITEM (разумеется, только размером sizeof(IO_WORKITEM)), которые "записываются" за соответствующим объектом устройства. Объект IO_WORKITEM инициализируется вызовом IoQueueWorkItem, который связывает с ним callback-процедуру драйвера и передаваемый при ее вызове контекстный аргумент. Вызов IoQueueWorkItem также помещает объект IO_WORKITEM в очередь объектов, тип которой определяется значением третьего параметра, то есть QueueType. В конце работы callback-процедура драйвера должна выполнить освобождение созданного объекта IO_WORKITEM вызовом IoFreeWorkItem.

Таблица 10.4. Прототип вызова IoQueueWorkItem

VOID IoQueueWorkItem IRQL&#60=DISPATCH_LEVEL
Параметры Инициализирует объект рабочего потока и помещает его в очередь (обычно используется сразу после вызова IoAllocateWorkItem)
IN PIO_WORKITEM pWorkItem Объект рабочего потока, созданный вызовом IoAllocateWorkItem
IN PIO_WORKITEM_ROUTINE

pWorkRoutine
Callback-процедура, предоставляемая драйвером (ее прототип описан в таблице 10.5)
IN WORK_QUEUE_TYPE QueueType Тип очереди. Драйвер должен предоставить одно из значений:

CriticalWorkQueue

DelayedWorkQueue
IN PVOID pContext Контекстный аргумент. Его получит callback-функция при вызове
Возвращаемое значение void
В том случае, если параметр QueueType при вызове будет равен CriticalWorkQueue, то объект IO_WORKITEM будет помещен в очередь для объектов с приоритетом в диапазоне RealTime (таблица 6.2), при значении DelayedWorkQueue

&#8212 в очередь объектов с приоритетом Normal.

Следует помнить, что число объектов IO_WORKITEM, которые операционная система позволяет получить каждому объекту устройства (соответственно, разместить в своих очередях) не бесконечно, поэтому следует проверять результат вызова IoAllocateWorkItem

на равенство NULL. Кроме того, не рекомендуется надолго задерживаться в callback-функции, поскольку это может затормозить извлечение из соответствующей очереди других IO_WORKITEM объектов, принадлежащих другим драйверам.


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

Термин 'системные рабочие потоки' (system worker threads) нельзя считать удачным, потому что он в точности копирует термин API пользовательского режима, обозначающий программные потоки пользовательского режима, которые уже никакими временными ограничениями не стеснены. Кроме того, лексическое отличие от "нормальных" системных программных потоков, с описания которых началась данная глава, просто неуловимо.
Таблица 10.5. Прототип callback-функции рабочего потока

VOID workCallback IRQL == PASSIVE_LEVEL
Параметры Функция, предоставляемая драйвером, которая будет вызвана при извлечении из очереди объекта IO_WORKITEM (вызывается в контексте, как для системного программного потока, см. выше)
IN PDEVICE_OBJECT pDevObject Объект устройства, которому принадлежит извлеченный из очереди объект IO_WORKITEM
IN PVOID pContext Контекстный аргумент &#8212 для получения дополнительной информации, "запланированной" при вызове IoQueueWorkItem

(например, указатель на IRP пакет и т.п.)
Возвращаемое значение void
Вызываемая callback-функция должна освобождать IO_WORKITEM объект вызовом IoFreeWorkItem, таблица 10.6.

Таблица 10.6. Прототип вызова IoFreeWorkItem

VOID IoFreeWorkItem IRQL&#60=DISPATCH_LEVEL
Параметры Удаляет объект рабочего потока
PIO_WORKITEM pWorkItem Объект рабочего потока, созданный вызовом ранее IoAllocateWorkItem
Возвращаемое значение void
Драйвер не должен делать какие-либо предположения о внутренней организации объектов IO_WORKITEM и изменять данные внутри. Для работы с этими объектами следует использовать только описанные выше вызовы.

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