Функции управления очередью низкого уровня

Для организации очереди с помощью функций низкого уровня используется стандартная структура LIST_ENTRY.

typedef struct _LIST_ENTRY {

struct _LIST_ENTRY *volatile Flink; // Указатель на следующий элемент списка

struct _LIST_ENTRY *volatile Blink; // Указатель на предыдущий элемент списка

} LIST_ENTRY, *PLIST_ENTRY;

Очередь, как это видно из определения структуры, двунаправленная.

В структуре DeviceExtension обычно создается экземпляр структуры LIST_ENTRY, представляющей голову очереди, который затем инициализируется с помощью функции InitializeListHead(). После этого можно добавлять или удалять записи в очередь. Для этого используются функции InsertHeadList(), InsertTailList(), RemoveHeadList(), RemoveTailList(), RemoveEntryList().

Пакеты IRP добавляются в очередь в диспетчерской функции, скорее всего работающей на уровне IRQL = PASSIVE_LEVEL. При этом выниматься из очереди они могут функциями, работающими на любом уровне IRQL, причем функции, выбирающие IRP из очереди, могут вытеснять функции, помещающие IRP в очередь. Возникает проблема синхронизации. Если ее не решить, незаконченная операция помещения IRP в очередь может быть прервана операцией выборки IRP из очереди, что приведет к появлению синего экрана.

Синхронизация доступа к разделяемому ресурсу производится с помощью спин-блокировки (механизмы синхронизации будут рассмотрены на следующей лекции). Поскольку операции добавления и удаления записей в очередь на уровнях IRQL PASSIVE_LEVEL и DISPATCH_LEVEL очень распространены, для их безопасного осуществления предусмотрены специальные функции: ExInterlocked…List(). Для использования этих функций должна быть создана и инициализирована спин-блокировка. Создается она обычно там же, где и голова очереди (обычно в DeviceExtension), и инициализируется после инициализации головы очереди. Например:

typedef struct _DEVICE_EXTENSION

{

...

LIST_ENTRY ListHead;

KSPIN_LOCK ListLock;

...

}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

//В функции DriverEntry

InitializeListHead(&(pDeviceExtension->ListHead));

KeInitializeSpinLock(&(pDeviceExtension->ListLock));

//после этого можно добавлять и удалять записи

PLIST_ENTRY pOldHead = ExInterlockedInsertHeadList(

&(pDeviceExtension->ListHead),

pNewListEntry,

&(pDeviceExtension->ListLock));

Как видно из определения структуры LIST_ENTRY, она не содержит полей для хранения собственно данных (например, указателя на пакет IRP). Поэтому распространенный способ использования структуры LIST_ENTRY – включение ее экземпляра в состав более общей структуры.

Для организации очереди пакетов IRP, в каждом пакете IRP в поле Tail.Overlay.ListEntry содержится экземпляр структуры LIST_ENTRY. При этом встает вопрос, как, зная указатель на структуру LIST_ENTRY, получить указатель на структуру IRP, в состав которой входит LIST_ENTRY. Для этого DDK предоставляет специальный макрос CONTAINING_RECORD

#define CONTAINING_RECORD (address, type, field) \

((type *)((PCHAR)(address) – (ULONG_PTR)(&((type *)0)->field)))

address: Известный адрес некоторого поля структуры, адрес которой необходимо получить.

type: Тип структуры, адрес которой необходимо получить.

field: Имя поля внутри искомой структуры, адрес этого поля передан в параметре address.

Применительно к IRP, мы должны будем написать что-то вроде

PListEntry = ExInterlockedRemoveHeadList(

&(pDeviceExtension->ListHead),

&(pDeviceExtension->ListLock));

pIrp = CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);

//далее – обработка IRP

Организация очереди пакетов IRP показана на следующем рисунке.

Рис. 1

[10.2.2.2.2] Функции управления очередью высокого уровня – “Очередь Устройства” (Device Queue)

Драйвер создает дополнительные Очереди Устройства с помощью выделения памяти из невыгружаемой памяти под дополнительные объекты-Очереди Устройства KDEVICE_QUEUE и инициализирует эти объекты с помощью функции KeInitializeDeviceQueue(). Добавление пакетов IRP в эти очереди производится с помощью функци KeInsertDeviceQueue() или KeInsertByKeyDeviceQueue(), а выборка пакетов из очереди - KeRemoveDeviceQueue(), KeRemoveByKeyDeviceQueue() или KeRemoveEntryDeviceQueue().

Для организации очереди пакетов IRP используется структура типа KDEVICE_QUEUE_ENTRY, указатель на которую содержится в пакете IRP в поле Tail.Overlay.DeviceQueueEntry.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: