[Previous] [Next]

Пакеты IRР

Практически весь процесс ввода/вывода, имеющий место в Windows, является пакетно-управляемым. Отдельная транзакция ввода/вывода описывается рабочим рецептом, предписывающим драйверу, что делать. При помощи IRP прослеживается также обработка запроса в подсистеме ввода/вывода. Этот рабочий рецепт имеет форму структуры данных, называемой I/o Request Packet (IRP) — пакет запроса на ввод/вывод.

При каждом запросе из программного кода клиента драйвера на выполнение операции ввода/вывода, включая IOCTL запросы (управляющие воздействия на аппаратуру), Диспетчер ввода/вывода выделяет под IRP область нестраничной памяти. Определив по дескриптору открытого файла, к какому драйверу и объекту устройства адресовано обращение, и по запрошенному коду операции ввода/вывода (IRP_MJ_Xxx), Диспетчер передает сформированный пакет IRP в соответствующую рабочую (см.ниже) процедуру драйвера. (Следует отметить, что для доступа из программного кода клиента применяется "файловая абстракция" процесса взаимодействия с драйвером — открытие, чтение, запись, дескрипторы и т.п.)

Пакеты IRP являются структурами данных переменной длины, и состоят из стандартного заголовка, содержащего общую учетную информацию, и одного или нескольких блоков параметров, называемых I/O stack location — ячейкой стека ввода/вывода.

Рис. 8.1
Структура пакета IRP

Заголовок IRP

Ниже перечислены поля заголовка, к которым можно обращаться из программного кода драйвера. К другим полям заголовка обращаться не рекомендуется.

Таблица 8.5. Заголовок пакета IRP

Поля Описание
IO_STATUS_BLOCK IoStatus Код состояния (статус) запроса
PVOID AssociatedIrp.SystemBuffer Указатель на системный буфер для случая, если устройство поддерживает буферизированный ввод/вывод
PMDL MdlAddress Указатель на MDL список в случае, если устройство поддерживает прямой ввод/вывод
PVOID UserBuffer Адрес пользовательского буфера для ввода/вывода
BOOLEAN Cancel Индикатор того, что пакет IRP должен быть аннулирован

Фрагмент структуры IRP под названием IoStatus фиксирует окончательное состояние данной операции ввода/вывода. Когда драйвер готов завершить обработку пакета IRP, он устанавливает в поле IoStatus.Status значение STATUS_XXX. В поле IoStatus.Information этого блока записывается 0 (если произошла ошибка) или другое определенное операцией ввода/вывода значение, чаще всего — количество переданных/полученных байт данных (которое может быть и равно нулю).

Вопросы адресации и доступа к буферам данных, описываемых пакетом IRP, будут рассмотрены позже.

Ячейки стека ввода/вывода

Основное назначение ячеек стека ввода/вывода (I/O stack location) состоит в том, чтобы хранить функциональный код и параметры запроса на ввод/вывод (последние могут претерпевать изменения при путешествии пакета по стеку драйверов). Ниже приводятся поля ячеек стека ввода/вывода, к которым драйвер может обращаться непосредственно по указателю (чего не рекомендуется делать для остальных полей).

Таблица 8.6. Некоторые элементы ячейки стека ввода/вывода

IO_STACK_LOCATION, *PIO_STACK_LOCATION
Поля Описание
UCHAR MajorFunction Код IRP_MJ_XXX, описывающий назначение операции
UCHAR MinorFunction Суб-код операции
PDEVICE_OBJECT
DeviceObject
Указатель на объект устройства, которому был адресован данный запрос IRP
PFILE_OBJECT FileObject Файловый объект для данного запроса, если он задан
union Parameters (трактовка определяется значением MajorFunction):
struct Read Параметры для IRP типа IRP_MJ_READ:
• ULONG Length
• ULONG Key
• LARGE_INTEGER ByteOffset
struct Write Параметры для IRP типа IRP_MJ_WRITE:
• ULONG Length
• ULONG Key
• LARGE_INTEGER ByteOffset
struct DeviceIoControl

Параметры для IRP типа IRP_MJ_DEVICE_CONTROL:
• ULONG OutputBufferLenght
• ULONG InputBufferLenght
• ULONG IoControlCode
• PVOID Type3InputBuffer

Для запроса, который адресован драйверу самого нижнего уровня, соответствующий IRP пакет имеет только одну ячейку стека. Для запроса, который послан драйверу верхнего уровня, Диспетчер ввода/вывода создает пакет IRP с несколькими стековыми ячейками — по одной на каждый драйверный слой. Другими словами, размер стека в пакете IRP численно равен количеству драйверных слоев, участвующих в обработке запроса на данную операцию. Любому драйверу в иерархии разрешен доступ только к его собственной — "числящейся" за ним — ячейке стека пакета IRP (хотя никто не контролирует и иные "противоправные" действия).

В случае если текущий драйвер решит обратиться к драйверу более низкого уровня с новым пакетом IRP собственного производства, то он должен гарантировать, что в новом IRP пакете имеется достаточное количество стековых ячеек.

Когда драйвер передает IRP пакет нижнему драйверному уровню, Диспетчер ввода/вывода автоматически изменяет указатель стека ввода/вывода IRP пакета таким образом, что он указывает на стековую ячейку, предназначенную для драйвера очередного нижнего уровня. Когда обработка пакета драйвером нижнего уровня завершена, и он "отпускает" пакет IRP, указатель стека снова возвращается в исходное положение и указывает на ячейку стека для лежащего выше драйвера. Разумеется, для получения указателя на текущую ячейку стека существует специальный системный вызов IoGetCurrentStackLocation.