Функції управління чергою низького рівня - студопедія
Для організації черги за допомогою функцій низького рівня використовується стандартна структура LIST_ENTRY.
typedef struct _LIST_ENTRY
struct _LIST_ENTRY * volatile Flink; // Покажчик на наступний елемент списку
struct _LIST_ENTRY * volatile Blink; // Покажчик на попередній елемент списку
Черга, як це видно з визначення структури, двунаправленная.
У структурі 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
// У функції DriverEntry
// після цього можна додавати і видаляти записи
PLIST_ENTRY pOldHead = ExInterlockedInsertHeadList (
Як видно з визначення структури 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)))
Стосовно до IRP, ми повинні будемо написати щось на кшталт
pIrp = CONTAINING_RECORD (pListEntry, IRP, Tail.Overlay.ListEntry);
// далі - обробка IRP
Організація черги пакетів IRP показана на наступному малюнку.
[10.2.2.2.2] Функції управління чергою високого рівня - "Черга Пристрої" (Device Queue)
Драйвер створює додаткові Черги Пристрої з допомогою виділення пам'яті з невивантажуваного пам'яті під додаткові об'єкти-Черги Пристрої KDEVICE_QUEUE і ініціалізує ці об'єкти за допомогою функції KeInitializeDeviceQueue (). Додавання пакетів IRP в ці черги проводиться за допомогою функціонально KeInsertDeviceQueue () або KeInsertByKeyDeviceQueue (), а вибірка пакетів з черги - KeRemoveDeviceQueue (), KeRemoveByKeyDeviceQueue () або KeRemoveEntryDeviceQueue ().
Для організації черги пакетів IRP використовується структура типу KDEVICE_QUEUE_ENTRY, покажчик на яку міститься в пакеті IRP в поле Tail.Overlay.DeviceQueueEntry.