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

Для організації черги за допомогою функцій низького рівня використовується стандартна структура 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.

Схожі статті