Функции управления очередью низкого уровня
Функции управления очередью низкого уровня
Для организации очереди с помощью функций низкого уровня используется стандартная структура LISTJENTRY.
typedef struct _LIST_ENTRY {
struct _LISTJ5NTRY ^volatile Flink; // Указатель
// на следующий элемент списка
struct _LIST_ENTRY ^volatile Blink; // Указатель
// на предыдущий элемент списка
} LIST_ENTRY, *PLIST_ENTRY;
Очередь, как это видно из определения структуры, двунаправленная.
В структуре DeviceExtension обычно создается экземпляр структуры LISTJENTRY, представляющей голову очереди, который затем инициализируется с помощью функции 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) ) ;
KelnitializeSpinLock (& (pDeviceExtension->ListLock) ) ;
//после этого можно добавлять и удалять записи
PLIST_ENTRY pOldHead = ExInterlockedlnsertHeadList ( & (pDeviceExtension->ListHead) , pNewListEntry, & (pDeviceExte'nsion->ListLock) ) ;
Как видно из определения структуры LIST_ENTRY, она не содержит полей для хранения собственно данных (например, указателя на пакет IRP). Поэтому распространенный способ использования структуры LIST_ENTRY - включение ее экземпляра в состав более общей структуры.
Для организации очереди пакетов IRP, в каждом пакете IRP в поле Tail.Over-lay.ListEntry содержится экземпляр структуры LIST_ENTRY. При этом встает вопрос, как, зная указатель на структуру LIST_ENTRY, получить указатель на структуру IRP, в состав которой входит LIST_ENTRY. Для этого DDK предоставляет специальный макрос CONTAINING_RECORD:
#define CONTAINING_RECORD (address, type, field) \
((type *) ( (PCHAR) (address) - (ULONG_PTR) (&((type *) 0) ->f ield) ) )
Где: Address - Известный адрес некоторого поля структуры, адрес которой необходимо получить;
Туре - Тип структуры, адрес которой необходимо получить;
Field- Имя поля внутри искомой структуры, адрес этого поля передан в параметре address.
Применительно к IRP, мы должны будем написать что-то вроде:
PListEntry = ExInterlockedRemoveHeadList ( & (pDeviceExtension->ListHead) , & (pDeviceExtension->ListLock) ) ;
plrp = CONTAINING_RECORD(pListEntry, IRP, Tail. Overlay. ListEntry) ; //далее - обработка IRP
Организация очереди пакетов IRP показана на Рисунок 12.