Системные рабочие потоки
Для нерегулярных коротких операций на уровне 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;
Данная структура создается и удаляется драйвером, что позволяет ей иметь нестандартную длину — главное, что начальный блок используется обычным для системы способом (как для WORK_QUEUE_ITEM).
Таблица 10.3. Прототип вызова IoAllocateWorkItem
PIO_WORKITEM IoAllocateWorkItem | IRQL<=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<=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 |
— в очередь объектов с приоритетом Normal.
Следует помнить, что число объектов IO_WORKITEM, которые операционная система позволяет получить каждому объекту устройства (соответственно, разместить в своих очередях) не бесконечно, поэтому следует проверять результат вызова IoAllocateWorkItem
на равенство NULL. Кроме того, не рекомендуется надолго задерживаться в callback-функции, поскольку это может затормозить извлечение из соответствующей очереди других IO_WORKITEM объектов, принадлежащих другим драйверам.
В частности, не рекомендуется из таких потоков обращаться к другим драйверам вызовом IoCallDriver. Для выполнения продолжительных операций рекомендуется использовать полноценные системные программные потоки, создаваемые вызовом PsCreateSystemThread.
![]() |
Термин 'системные рабочие потоки' (system worker threads) нельзя считать удачным, потому что он в точности копирует термин API пользовательского режима, обозначающий программные потоки пользовательского режима, которые уже никакими временными ограничениями не стеснены. Кроме того, лексическое отличие от "нормальных" системных программных потоков, с описания которых началась данная глава, просто неуловимо. |
VOID workCallback | IRQL == PASSIVE_LEVEL |
Параметры | Функция, предоставляемая драйвером, которая будет вызвана при извлечении из очереди объекта IO_WORKITEM (вызывается в контексте, как для системного программного потока, см. выше) |
IN PDEVICE_OBJECT pDevObject | Объект устройства, которому принадлежит извлеченный из очереди объект IO_WORKITEM |
IN PVOID pContext | Контекстный аргумент — для получения дополнительной информации, "запланированной" при вызове IoQueueWorkItem (например, указатель на IRP пакет и т.п.) |
Возвращаемое значение | void |
Таблица 10.6. Прототип вызова IoFreeWorkItem
VOID IoFreeWorkItem | IRQL<=DISPATCH_LEVEL |
Параметры | Удаляет объект рабочего потока |
PIO_WORKITEM pWorkItem | Объект рабочего потока, созданный вызовом ранее IoAllocateWorkItem |
Возвращаемое значение | void |
![]() |
Драйвер не должен делать какие-либо предположения о внутренней организации объектов IO_WORKITEM и изменять данные внутри. Для работы с этими объектами следует использовать только описанные выше вызовы. |