- •Введение
- •Подсистема ввода-вывода: общие принципы построения и работы
- •1.1. Взаимодействие процессора с внешними устройствами
- •1.2. Прямой доступ к памяти
- •Драйверы
- •Роль драйверов в операционной системе
- •Взаимодействие драйверов с компонентами операционной системы и пользовательскими программами
- •Стек обработки запросов ввода-вывода
- •Основы организации и работы подсистемы ввода-вывода unix
- •2.1. Драйверы в операционных системах семейства unix
- •Стратегическая функция драйвера блочного устройства
- •Функция обработки прерывания
- •Функция опроса устройства
- •Другие функции драйверов
- •Буферизация в символьных драйверах
- •Терминальный драйвер
- •2.2. Потоковая подсистема ввода-вывода в unix
- •Архитектура и принципы работы подсистемы streams
- •Архитектура и работа модулей потока
- •Функция модуля put
- •Функция модуля service
- •Структура сообщения
- •Основы организации и работы подсистемы ввода-вывода windows
- •3.1. Классификаций драйверов Windows
- •Драйверы пользовательского режима
- •Драйверы режима ядра
- •3.2. Объекты подсистемы ввода-вывода
- •Объект файл
- •Объект устройство
- •Объект драйвер
- •Объект пакет запроса ввода-вывода
- •Объект блок стека запросов ввода-вывода
- •3.3. Передача данных между пользовательским адресным пространством и пространством ядра
- •Буферизированный ввод-вывод
- •Прямой ввод-вывод
- •Ввод-вывод под управлением драйвера
- •3.4. Обработка запросов ввода-вывода
- •Прохождение запроса ввода-вывода вниз через стек обработки запросов ввода-вывода
- •Обработка прерывания по завершению ввода-вывода
- •Обратное прохождение запроса ввода-вывода вверх через стек запросов ввода-вывода
- •3.5. Буферизация запросов ввода-вывода
- •Системная очередь запросов
- •Очереди запросов под управлением драйвера
- •3.6. Диспетчер Plug-And-Play, установка и запуск драйверов
- •3.7. Диспетчер электропитания
- •3.8. Среда сетевых драйверов ndis
- •Драйверы среды ndis Минипорт-драйверы сетевых адаптеров
- •Драйверы протоколов
- •Промежуточные драйверы
- •Структура ndis пакета
- •Запросы к сетевым адаптерам
- •3.9. Порты завершения ввода-вывода
- •Заключение
- •Библиографический список
- •Оглавление
- •394026 Воронеж, Московский просп., 14
Прямой ввод-вывод
При использовании прямого ввода-вывода диспетчер ввода-вывода создает список 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 станет равным нулю, основной запрос автоматически завершится диспетчером ввода-вывода.