Передача данных от приложения к драйверу. Асинхронная обработка
Код пользовательского уровня не может напрямую вызвать
код режима ядра. Для этого существуют специальные прерывания. Одним из
них является прерывание 2Е -вызов системного сервиса. Диспетчер ввода/вывода
обрабатывает вызовы системных сервисов специальным образом (см. рис. 9).
В своем обработчике системного сервиса он создает специальный запрос ввода/вывода
IRP и передает его на обработку некоторому объекту-устройству, после чего
работа обработчика может завершиться, но обработка IRP при этом может
быть не закончена.
Рис.9
Модель ввода/вывода, предусматривающая завершение функции
ввода/вывода до завершения запроса ввода/вывода называется асинхронным
вводом/выводом. Асинхронный ввод/вывод позволяет программе запросить выполнение
операции ввода/вывода, после чего продолжать выполнение другой операции, пока устройство не закончит пересылку данных. Система ввода/вывода автоматически уведомляет программу о завершении операции ввода/вывода.
Модель ввода/вывода, обеспечиваемая Диспетчером ввода/вывода, асинхронна всегда, хотя этот факт может быть скрыт функциями подсистемы окружения. Например, так происходит в случае функции CreateFile(). Эта функция не является асинхронной, хотя пользуется асинхронной функцией NtCreateFile().
При асинхронном вводе/выводе более поздний по времени запрос ввода/вывода может быть закончен до завершения более ранних запросов.
Вызов прерывания и переключение процессора в режим ядра не влияют на текущий контекст памяти. Из этого следует, что при обработке вызова системного сервиса и Диспетчер памяти, и соответствующая точка входа драйвера работают в контексте памяти процессора - инициатора запроса ввода/вывода.
В случае, когда устройство обрабатывает запрос ввода/вывода сразу при его поступлении, диспетчеру ввода/вывода не требуется переключение контекста памяти для уведомления процесса о завершении запроса.
В случае, когда устройство отложило запрос в очередь запросов, в тот момент, когда подошла очередь запроса на обработку, контекст памяти будет неизвестен - случайный контекст памяти. При этом диспетчеру ввода/вывода понадобится механизм уведомления нужного процесса о завершении запроса ввода/вывода. Таким механизмом является механизм Асинхронного Вызова Процедуры (Asynchronous procedure call, АРС). Вкратце он состоит в том, что прикладная программа предоставляет диспетчеру ввода/вывода адрес функции, которая должна быть вызвана при завершении запроса ввода/вывода. При запросе АРС диспетчер ввода/вывода указывает этот адрес и поток, в котором должна быть вызвана эта функция. АРС запрашивается с помощью генерации специального прерывания на уровне IRQL равном APC_LEVEL. Запрос АРС откладывается в очередь АРС и будет выполнен, когда управление получит нужный поток и текущий уровень IRQL будет меньше APC_LEVEL.
Выполнение асинхронного запроса
Выполняя асинхронный ввод/вывод, поток пользовательского режима может использовать для синхронизации с моментом завершения операции ввода/вывода:
- 1. ожидание у описателя файла;
- 2. ожидание у объекта-события, используемого для каждого запроса ввода/вывода;
- 3. асинхронный вызов процедуры (Asynchronous procedure call, АРС) пользовательского режима.
Выполнение асинхронного запроса приводится на примере
запроса на запись, проходящего через несколько слоев драйверов. При этом
добавляется один или несколько уровней обработки.
Вместо повторного использования одного IRP, драйвер верхнего уровня может
создать группу ассоциированных IRP, которые будут управлять одним запросом
параллельно. Этот драйвер отслеживает завершение всех ассоциированных
IRP, и только потом завершает исходный IRP.
- 1. Подсистема среды или клиентская DLL вызывает функцию «родного» API NtWriteFile() с описателем файлового объекта.
- 2. Диспетчер ввода/вывода создает IRP, в котором он сохраняет указатель на файловый объект и код функции, указывающий драйверу верхнего уровня тип операции. Далее диспетчер ввода/вывода отыскивает драйвер верхнего уровня и передает ему IRP.
- 3. Драйвер верхнего уровня определяет по информации в соответствующей ему области стека IRP, какую операцию он должен выполнить. Драйвер верхнего уровня может, либо разбить первоначальный запрос на несколько запросов, путем размещения новых пакетов IRP (возможно для нескольких драйверов устройств), либо может повторно использовать первоначальный IRP, заполнив область стека IRP, соответствующую нижележащему драйверу. Затем драйвер передает этот IRP (или несколько ассоциированных IRP) посредством вызова диспетчера ввода/вывода.
- 4. Вызванный драйвер нижнего уровня (пусть это будет уже драйвер устройства) проверяет свою область стека IRP, чтобы определить какую операцию он должен выполнить на устройстве, и в поле статуса операции ввода/вывода в пакете IRP ставит код «ввод/вывод выполняется». Затем он вызывает функцию loStartPacket(), реализуемую менеджером ввода/вывода.
- 5. Диспетчер ввода/вывода определяет, занято ли уже устройство обработкой другого IRP, и если да, то ставит текущий IRP в очередь устройства и возвращает управление. Если нет, то диспетчер ввода/вывода передает IRP соответствующей процедуре драйвера устройства, которая начинает операцию ввода/вывода на устройстве. Начав операцию на устройстве, драйвер устройства возвращает управление. Заметьте, что асинхронный запрос ввода/вывода возвращает управление вызывающей программе немедленно, даже если очередь устройства пуста.
Вторая стадия обработки запроса ввода/вывода, состоит в обслуживании прерывания от устройства:
- 1. После завершения передачи данных устройство генерирует прерывание для обслуживания. Диспетчер прерываний ядра передает управление процедуре обслуживания прерываний (Interrupt Service Routine, ISR) драйвера устройства.
- 2. ISR выполняет минимум работы по сохранению необходимого контекста
операции. ISR также вызывает соответствующие сервисы диспетчера ввода/вывода
для постановки в очередь отложенного вызова процедуры (Deferred Procedure
Call, DPC). DPC - асинхронная процедура драйвера устройства, выполняющая
завершение требуемой операции при более низком уровне IRQL процессора.
Прерывания от устройств имеют высокий IRQL, но IRQL процессора, выполняющего ISR, остается на этом уровне только на время, необходимое для того, чтобы запретить новые прерывания от устройства. После завершения ISR, ядро понижает IRQL процессора до уровня, на котором тот находился до прерывания. - 3. Если IRQL процессора понизится ниже уровня планирования потоков и обработки отложенного вызова процедуры (Dispatch level), возникнет прерывание DPC, и диспетчер прерываний передаст управление процедуре DPC драйвера устройства.
- 4. Процедура DPC использует для завершения операции сохраненный в процедуре ISR контекст операции, запоминает статус завершившейся операции и выбирает из очереди следующий пакет IRP, запустив тем самым новый запрос ввода/вывода. Затем устанавливает статус только что завершенной операции в поле статуса IRP и вызывает диспетчер ввода/вывода для завершения обработки запроса и удаления IRP.
- 5. Диспетчер ввода/вывода обнуляет область стека IRP, соответствующую драйверу устройства, и вызывает завершающую процедуру драйвера верхнего уровня. Эта процедура проверяет поле статуса операции, чтобы определить, нужно ли повторить запрос, или можно освободить этот IRP (если это IRP было размещено этим драйвером). Драйвер верхнего уровня собирает информацию о статусах операций для всех размещенных им пакетов IRP, чтобы установить общий статус в первоначальном пакете IRP и завершить его.
- 6. Диспетчер ввода/вывода ставит в очередь АРС-объект (Asynchronous Procedure Call - асинхронный вызов процедуры) для завершения ввода/вывода в контексте потока - инициатора запроса. (Подсистема ввода/вывода должна скопировать некоторые данные из системной области памяти в виртуальное адресное пространство процесса, поток которого инициировал запрос ввода/вывода, во время исполнения такого потока, это достигается путем пересылки АРС-объекта режима ядра в очередь АРС-объек-тов этого потока.)
- 7. Когда поток - инициатор запроса, начнет исполняться в следующий раз, то возникнет прерывание обработки асинхронного вызова процедуры. Диспетчер прерываний передает управление процедуре АРС режима ядра (процедуре АРС диспетчера ввода/вывода).
- 8. Процедура АРС режима ядра записывает данные в адресное пространство потока-инициатора запроса, устанавливает описатель файла в состояние «свободен», устанавливает в очередь на исполнение АРС-объект пользовательского режима (если нужно) и удаляет IRP.