Существует два типа АРС: вызов в режиме ядра и вызов в пользовательском режиме. Асинхронный вызов процедуры в режиме ядра связан с драйвером или другим кодом режима ядра и обычно используется для передачи данных, например для копирования данных из буфера ядра в пользовательский буфер. Помните, что пользовательский буфер должен быть доступен в контексте процесса, который владеет буфером.
Код пользовательского режима тоже может использовать асинхронный вызов процедур. Для этого необходим прикладной интерфейс программирования QueueUserAPC, который рассматривается в документации к набору Platform SDK. Асинхронные вызовы процедур в пользовательском режиме предоставляются только тогда, когда поток получает предупреждение, например при блокировании в результате вызова функций WaitForSingleObject или WaitForMultipleObject. Подробная информация об этих функциях доступна в документации Platform SDK. Достаточно сказать, что эти функции позволяют организовать синхронизацию потоков.
Асинхронный вызов процедуры может быть блокирующим, например для выполнения специального ввода-вывода. Вызовы помещаются в очередь, соответствующую потоку, т.е. существует несколько очередей асинхронных вызовов процедур.
1.6 Драйверы и буферы ввода-вывода
В этом разделе рассматриваются буферы ввода-вывода, которые уже упоминались ранее в главе. Драйверы используют буферы для осуществления ввода-вывода и управления им (IOCTL). Для этого драйверы посредством соответствующего объекта указывают предпочтительный способ ввода-вывода. Существует три поддерживаемых драйверами Windows NT типа ввода- вывода: буферизированный, прямой и небуферизированный. Эти методы рассматриваются в данном разделе.
Буферизированный ввод-вывод обычно используется для передачи меньших объемов данных, так как в процессе ввода-вывода участвуют операции копирования данных. После того как приложение отправило запрос на ввод- вывод, диспетчер ввода-вывода проверяет соответствие прав доступа приложения к предоставленному буферу ввода-вывода (соответствие прав доступа предполагает, что приложение имеет права на чтение и запись, а также имеет доступ к буферу необходимого размера, указанного в запросе на ввод- вывод). Диспетчер ввода-вывода выделяет буфер в невыгружаемой памяти и в случае запроса на запись копирует данные из буфера приложения в выделенный буфер. Этот буфер передается драйверу.
Драйвер не обязан заботиться о контексте потока, так как буфер находится в невыгружаемой памяти и действителен в контексте любого процесса или потока. Драйвер выполняет необходимую операцию ввода-вывода. Для операции чтения драйвер копирует данные в полученный буфер. Кроме того, драйвер может «предположить», что в контексте виртуального адресного пространства буфер будет непрерывным, но, как и в случае других буферов Windows NT, непрерывность буфера в виртуальном адресном пространстве не гарантирует непрерывности в физическом адресном пространстве.
На этом этапе драйвер завершает обработку IRP, и ответственность за копирование данных из невыгружаемого буфера в буфер приложения возлагается на диспетчер ввода-вывода. Эта операция копирования должна выполняться в контексте процесса, который отправил запрос на ввод-вывод. Кроме того, диспетчер ввода-вывода должен освобождать буфер, выделенный в невыгружаемой области памяти.
Прямой ввод-вывод (Direct I/O) несколько более запутан, чем буферизированный, однако намного эффективнее при выполнении операций ввода- вывода для больших массивов данных. Диспетчер ввода-вывода выполняет базовые проверки, например проверяет разрешения приложения на доступ к буферу посредством области ввода-вывода желательного объема. Буфер в памяти, независимый от процесса, описывается для драйвера средствами структуры данных, которая называется список дескрипторов памяти (memory descriptor list – MDL). Адрес буфера используется в качестве общесистемного виртуального адресного пространства.
Операционная система Windows NT предоставляет процедуры драйверов, которые позволяют получать доступ к различным полям списка дескрипторов памяти. Создателям драйверов рекомендуется использовать список дескрипторов памяти в качестве целостного элемента. Дополнительная информация по использованию процедур работы со списками дескрипторов памяти приводится в документации к инструментарию создания драйверов (DDK) Windows NT. Процедуры, описанные в DDK, предоставляют следующие возможности: