Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие 3000377.doc
Скачиваний:
29
Добавлен:
30.04.2022
Размер:
2.52 Mб
Скачать

Прямой ввод-вывод

При использовании прямого ввода-вывода диспетчер ввода-вывода создает список MDL, описывающий размещение пользовательского буфера в физической памяти, и передает драйверу указатель на начало этого списка через поле MdlAddress структуры IRP. При передаче больших объемов данных прямой ввод-вывод реализуется эффективнее, чем буферизированный, за счет исключения промежуточного копирования данных в системный буфер. Но при передаче данных, объемом менее одной страницы памяти, использование прямого ввода-вывода не оправдано и следует применять буферизированный ввод-вывод.

Заметим, что для запроса IRP_MJ_DEVICE_CONTROL, предусматривающего двунаправленную передачу данных, при использовании прямого ввода-вывода, через список MDL драйвер получает только ссылку на выходной буфер, в котором драйвер должен разместить данные, передаваемые в пользовательскую программу. Данные входного буфера передается в драйвер через системный буфер, как и при буферизированном вводе-выводе.

Ввод-вывод под управлением драйвера

При использовании ввода-вывода под управлением драйвера, драйвер получает указатель на область памяти в пользовательском адресном пространстве через поле UserBuffer структуры IRP.

При этом необходимо учитывать, что адрес пользовательского буфера, переданный в драйвер, является корректным, пока драйвер выполняется в контексте процесса, из которого сделан запрос ввода-вывода. Поэтому, при необходимости доступа к данным из асинхронных процедур драйвера, когда контекст процесса не определен, например, из системного потока, созданного драйвером, необходимо скопировать данные в системный буфер или закрепить их в физической памяти, создав список MDL для них, или использовать процедуры APC, зарегистрированные для вызывающего потока.

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

Заметим, что для запроса IRP_MJ_DEVICE_CONTROL, предусматривающего двунаправленную передачу данных, при использовании ввода-вывода под управлением драйвера, через поле UserBuffer драйвер получает адрес выходного буфера, в котором он должен разместить данные, передаваемые в пользовательскую программу. Адрес входного буфера в пользовательском адресном пространстве передается в драйвер через поле Parameters.DeviceIoControl.Type3InputBuffer в структуре IO_STACK_LOCATION.

3.4. Обработка запросов ввода-вывода

Обработка запроса ввода-вывода включает три этапа:

  • прохождение запроса ввода-вывода вниз через стек обработки запросов ввода-вывода;

  • обработка прерывания по завершению ввода-вывода;

  • обратное прохождение запроса ввода-вывода вверх через стек запросов ввода-вывода.

Рассмотрим указанные этапы обработки более внимательно.

Прохождение запроса ввода-вывода вниз через стек обработки запросов ввода-вывода

После получения запроса ввода-вывода и генерации командного прерывания, диспетчер ввода-вывода продолжает работать в контексте вызывающего процесса. Он находит объект файл в таблице описателей текущего процесса по описателю файла, переданному в системный вызов. Диспетчер ввода-вывода создает IRP и блок стека IRP, содержащий код запрошенной операции.

Затем диспетчер ввода-вывода через объект файл находит объект устройство, к которому направлен запрос, а через объект устройство – объект драйвер. По типу запроса, диспетчер ввода-вывода вызывает нужную функцию драйвера по адресу из таблицы диспетчеризации в объекте драйвер.

В качестве аргументов при вызове функции диспетчеризации передается указатель на объект устройство, к которому направлен запрос, и указатель на IRP, сопоставленный с запросом.

Если текущий объект устройство не является последним устройством в стеке обработки запроса ввода-вывода, драйвер определяет следующее устройство в стеке по указателю AttachedDevice в текущем объекте устройство, подготавливает данные для нижележащего драйвера и передает ему управление посредством вызова функции режима ядра CallDriver.

На рис. .18 приведена схема вызова функции драйвера в ответ на запрос ввода-вывода, отправленный к файлу.

Рис.18. Вызов функции драйвера

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

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

Рис.19. Увеличение числа обращений к нижележащему драйверу при прохождении запроса через стек обработки запросов ввода-вывода

Таким образом, для обращения к нижележащим драйверам может потребоваться создание новых пакетов запроса ввода-вывода. Драйвер может создавать новые IRP с помощью функции режима ядра, определенной следующим образом:

PIRP IoAllocateIrp( IN CCHAR StackSize, // количество уровней в стеке IN BOOLEAN ChargeQuota // флаг контроля квоты потока );

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

Помимо создания независимых запросов ввода-вывода, передаваемых нижележащим драйверам, драйверы верхнего уровня, получающие запросы непосредственно от диспетчера ввода-вывода, например, драйверы файловых систем, могут использовать ассоциированные IRP, связанные с главным IRP, описывающим запрос ввода-вывода.

Для создания ассоциированного IRP драйвер должен вызвать следующую функцию:

PIRP IoMakeAssociatedIrp( IN PIRP Irp, // указатель на главный IRP IN CCHAR StackSize // количество уровней в стеке );

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

Перед вызовом IoMakeAssociateIrp, драйвер должен инициализировать поле IrpCount структуры IPR числом, соответствующим количеству ассоциированных IRP, которые будут созданы и сопоставлены с данным главным IRP.

Преимущество использования ассоциированных IRP вместо независимых запросов состоит в возможности автоматизации завершения обработки запроса ввода-вывода.

При использовании независимых запросов ввода-вывода, драйвер должен сам отслеживать завершение каждого созданного им запроса нижележащим драйвером, и после завершения всех таких запросов явно завершить основной запрос.

При использовании ассоциированных IRP вызывающий драйвер отправляет вниз по стеку все ассоциированные IRP и завершить диспетчерскую функцию со статусом основного IRP «незавершенная операция ввода-вывода» (STATUS_PENDING). При завершении каждого ассоциированного IRP будет автоматически декрементироваться поле IrpCount основного IRP, и как только IrpCount станет равным нулю, основной запрос автоматически завершится диспетчером ввода-вывода.