вывода разблокирует буфер. Диспетчер хранит описание этой памяти в форме MDL (memory descriptor list). MDL указывает объем физической памяти, занятой буфером (подробнее о MDL см. Windows DDK). Устройствам, использующим DMA (прямой доступ к памяти), требуется лишь физическое описание буфера, поэтому таким устройствам достаточно MDL. (Устройства, поддерживающие DMA, передают данные в память компьютера напрямую, не используя процессор.) Ho, если драйверу нужен доступ к содержимому буфера, он может спроецировать его на системное адресное пространство.

(o) Ввод-вывод без управления (neither I/O) Диспетчер ввода- вывода не участвует в управлении буферами. Ответственность за управление ими возлагается на драйвер устройства.

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

Когда вызывающие потоки передают запросы размером менее одной страницы (4 Кб на х86- процессорах), драйверы, как правило, используют буферизованный ввод-вывод, а для запросов большего размера – прямой. Буфер примерно равен размеру страницы, и операция копирования с применением буферизованного ввода-вывода приводит практически к тем же издержкам, что и прямой ввод-вывод, требующий блокирования памяти. Драйверы файловой системы обычно используют третий тип управления, так как при копировании данных из кэша файловой системы в буфер вызывающего потока это позволяет избавиться от издержек, связанных с управлением буферами. Ho большинство драйверов не использует этот вид управления из-за того, что указатель на буфер вызывающего потока действителен лишь на то время, пока выполняется этот поток. Если драйверу нужно передать данные с устройства (или на устройство) при выполнении DPC-процедуры или ISR, он должен позаботиться о доступности данных вызывающего потока из контекста любого процесса, а значит, у буфера должен быть системный виртуальный адрес.

Драйверы, использующие ввод-вывод без управления для доступа к буферам, которые могут быть расположены в пользовательском пространстве, должны проверять, что адреса буфера действительны и не ссылаются на память режима ядра. Если они этого не делают, появляется вероятность краха системы или уязвимости в системе защиты, так как приложения получают доступ к памяти режима ядра или возможность внедрения своего кода в ядро. Функции ProbeForRead и ProbeForWrite, которые ядро предоставляет драйверам, проверяют, полностью ли умещается буфер в пользовательской части адресного пространства. Чтобы избежать краха из-за ссылки на недопустимый адрес, драйверы могут обращаться к буферам пользовательского режима из кода обработки исключений (блоков try/except), который перехватывает любые попытки доступа по неправильным адресам и транслирует их в коды ошибок для передачи приложению.

Запрос ввода-вывода к одноуровневому драйверу

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

1. Запрос на ввод-вывод передается через DLL подсистемы.

2. DLL подсистемы вызывает сервис NtWriteFile диспетчера ввода-вывода.

3. Диспетчер ввода-вывода создает IRP, описывающий запрос, и посылает его драйверу (в данном случае – драйверу устройства), вызывая свою функцию IoCallDriver.

4. Драйвер передает данные из IRP на устройство и инициирует операцию ввода-вывода.

5. Драйвер уведомляет о завершении ввода-вывода, генерируя прерывание.

6. Когда устройство завершает операцию и вызывает прерывание, драйвер устройства обслуживает прерывание.

7. Драйвер вызывает функцию IoCompleteRequest диспетчера ввода-вывода, чтобы уведомить его о завершении обработки IRP, и диспетчер ввода-вывода завершает данный запрос на ввод-вывод.

Эти семь этапов показаны на рис. 9-10.

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

Обслуживание прерывания

Завершая передачу данных, устройство генерирует прерывание, после чего в дело вступают ядро Windows, диспетчер ввода-вывода и драйвер устройства. Ha рис. 9-11 показана первая фаза этого процесса. (Механизм диспетчеризации прерываний, включая DPC, описывается в главе 3. Мы кратко повторяем этот материал, потому что DPC играют ключевую роль в обработке ввода-вывода.)

Когда устройство генерирует прерывание, процессор передает управление обработчику ловушки ядра, который находит ISR для этого устройства по таблице диспетчеризации прерываний. ISR в Windows обычно обрабатывают прерывания от устройств в два этапа. При первом вызове ISR, как правило, остается на уровне Device IRQL ровно столько времени, сколько нужно для того, чтобы сохранить состояние устройства и запретить дальнейшие прерывания от него. После этого ISR помещает DPC в очередь и, закрыв прерывание, завершается. Впоследствии, при вызове DPC-процедуры драйвер устройства заканчивает обработку прерывания, а затем вызывает диспетчер ввода-вывода для завершения ввода-вывода и удаления IRR Этот драйвер также может начать выполнение следующего запроса ввода-вывода, ждущего в очереди устройства.

Преимущество выполнения большей части обработки прерываний от устройств через DPC в том, что это разрешает любые блокируемые прерывания с приоритетами от «Device IRQL» до «DPC/dispatch» – пока не началась обработка DPC, имеющего более низкий приоритет. A за счет этого удается более оперативно (чем это могло бы быть в ином случае) обслуживать прерывания среднего приоритета. Вторая фаза ввода- вывода (обработка DPC) показана на рис. 9-12.

Завершение обработки запроса на ввод-вывод

После того как DPC-процедура драйвера выполнена, до завершения запроса на ввод-вывод остается проделать кое-какую оставшуюся работу. Третья стадия обработки ввода-вывода называется завершением ввода-вывода (I/O completion) и начинается с вызова драйвером функции IoСотрleteRequest для уведомления диспетчера ввода-вывода о том, что обработка запроса, указанного в IRP (и принадлежащих ему блоках стека), закончена. Действия, выполняемые на этом этапе, различны для разных операций ввода-вывода. Например, все сервисы ввода- вывода записывают результат операции в блок статуса ввода-вывода (I/O status block) – структуру данных, предоставляемую вызывающим потоком. Некоторые сервисы, выполняющие буферизованный ввод-вывод, требуют возврата данных вызывающему потоку через подсистему ввода-вывода.

B любом случае подсистема ввода-вывода должна копировать отдельные данные из системной памяти в виртуальное адресное пространство процесса, которому принадлежит вызывающий поток. Если IRP выполняется синхронно, это адресное пространство является текущим и доступно напрямую, но если IRP обрабатывается асинхронно, диспетчер ввода-вывода должен отложить завершение IRP до тех пор, пока у него не появится возможность обращаться к нужному адресному пространству. Чтобы получить доступ к виртуальному адресному пространству процесса, которому принадлежит вызывающий поток, диспетчер ввода-вывода должен передавать данные «в контексте вызывающего потока», т. е. при выполнении этого потока (иначе говоря, процесс этого потока должен быть текущим, а его адресное пространство – активным на процессоре). Эту задачу диспетчер ввода-вывода решает, ставя в очередь данному потоку APC режима ядра (рис. 9-13).

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату