Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Драйверы, реализующие собственную очередь IRP, должны использовать платформу , обеспечивающую безопасное отмену очереди IRP. Очереди IRP, обеспечивающие безопасность отмены, разделяют обработку IRP на две части.
Драйвер предоставляет набор процедур обратного вызова, реализующих стандартные операции в очереди запросов I/O драйвера. К указанным операциям относятся вставка и удаление IRP из очереди, блокировка и разблокировка очереди. См. реализацию Cancel-Safe очереди IRP.
Всякий раз, когда драйверу нужно вставить или удалить IRP из очереди, он использует предоставляемые системой подпрограммы IoCsqXxx. Эти подпрограммы обрабатывают всю синхронизацию и логику отмены IRP для драйвера.
Драйверы, использующие безопасные для отмены очереди IRP, не реализуют рутины отмены для поддержки отмены IRP.
Платформа гарантирует, что драйверы вставляют и удаляют IRP из своей очереди атомарно. Он также гарантирует правильность реализации отмены IRP. Драйверы, которые не используют платформу, должны вручную блокировать и разблокировать очередь перед выполнением любых вставок и удалений. Они также должны избежать возникновения условий гонки, которые могут возникнуть во время реализации процедуры отмены . (Описание условий гонки, которые могут возникнуть, см. в разделе Синхронизация отмены IRP.)
Платформа очередей IRP с безопасностью отмены включена в Windows XP и более поздних версиях Windows. Драйверы, которые также должны работать с Windows 2000 и Windows 98/Me, могут ссылаться на библиотеку Csq.lib, включенную в комплект драйверов Windows (WDK). Библиотека Csq.lib предоставляет реализацию этой платформы.
ПодпрограммыXXX IoCsq объявляются в Windows XP и более поздних версиях Wdm.h и Ntddk.h. Драйверы, которые также должны работать с Windows 2000 и Windows 98/Me, должны включать Csq.h для объявлений.
Вы можете увидеть полную демонстрацию использования очередей IRP, безопасных для отмены, в каталоге \src\general\cancel в WDK. Дополнительные сведения об этих очередях см. в потоке управления для Cancel-Safe очереди IRP техническом документе.
Реализация очереди IRP Cancel-Safe
Чтобы реализовать очередь IRP, безопасную для отмены, драйверы должны предоставить следующие подпрограммы:
Любой из следующих подпрограмм для помещения IRP в очередь: CsqInsertIrp или CsqInsertIrpEx. CsqInsertIrpEx — расширенная версия CsqInsertIrp; очередь реализуется с помощью одной или другой.
Рутина CsqRemoveIrp, которая удаляет указанный IRP из очереди.
Подпрограмма csqPeekNextIrp, которая возвращает указатель на следующий IRP, следующий за указанным IRP в очереди. В этом случае система передает значение PeekContext, которое оно получает от IoCsqRemoveNextIrp. Драйвер может интерпретировать это значение каким-либо образом.
Обе следующие подпрограммы позволяют системе блокировать и разблокировать очередь IRP: CsqAcquireLock и CsqReleaseLock.
CsqCompleteCanceledIrp — процесс, завершающий отмененный IRP.
Указатели на подпрограммы драйвера хранятся в структуре IO_CSQ, описывающей очередь. Драйвер выделяет хранилище для структуры IO_CSQ. Структура IO_CSQ гарантированно остается фиксированного размера, поэтому драйвер может безопасно включить её в расширение своего устройства.
Драйвер использует IoCsqInitialize или IoCsqInitializeEx для инициализации структуры. Используйте IoCsqInitialize, если очередь реализует CsqInsertIrp, или IoCsqInitializeEx, если очередь реализует CsqInsertIrpEx.
Драйверы должны предоставлять только основные функциональные возможности в каждой подпрограмме обратного вызова. Например, только рутины CsqAcquireLock и CsqReleaseLock реализуют обработку блокировки. Система автоматически вызывает эти подпрограммы для блокировки и разблокировки очереди по мере необходимости.
Вы можете реализовать любой тип механизма очереди IRP в драйвере, при условии предоставления подходящих диспетчерских подпрограмм. Например, драйвер может реализовать очередь в виде связанного списка или в качестве очереди приоритета.
CsqInsertIrpEx предоставляет более гибкий интерфейс для очереди, чем CsqInsertIrp. Драйвер может использовать возвращаемое значение, чтобы указать результат операции; если он возвращает код ошибки, это означает, что вставка не удалась. рутина CsqInsertIrp не возвращает значение, поэтому нет простого способа указать, что вставка не удалась. Кроме того, CsqInsertIrpEx принимает дополнительный параметр InsertContext, определенный драйвером, который можно использовать для указания дополнительных сведений, специфичных для драйвера, которые будут использоваться реализацией очереди.
Драйверы могут использовать CsqInsertIrpEx для реализации более сложной обработки IRP. Например, если ожидающие irPs отсутствуют, подпрограмма CsqInsertIrpEx может вернуть код ошибки, и драйвер может немедленно обработать IRP. Аналогичным образом, если IRP больше нельзя поставить в очередь, CsqInsertIrpEx может вернуть код ошибки, указывающий на это.
Драйвер изолирован от всей обработки отмены IRP. Система предоставляет подпрограмму отмена для отмены IRP в очереди. Эта подпрограмма вызывает CsqRemoveIrp для удаления IRP из очереди и CsqCompleteCanceledIrp для завершения отмены IRP.
На следующей схеме показан поток управления для отмены IRP.
Базовая реализация CsqCompleteCanceledIrp выглядит следующим образом.
VOID CsqCompleteCanceledIrp(PIO_CSQ Csq, PIRP Irp) {
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
Драйверы могут использовать любой из примитивов синхронизации операционной системы для реализации своих процедур CsqAcquireLock и CsqReleaseLock. Доступные примитивы синхронизации включают спин-блокировки и объекты мьютекс.
Ниже приведен пример того, как драйвер может реализовать блокировку с помощью спин-блокировок.
/*
The driver has previously initialized the SpinLock variable with
KeInitializeSpinLock.
*/
VOID CsqAcquireLock(PIO_CSQ IoCsq, PKIRQL PIrql)
{
KeAcquireSpinLock(SpinLock, PIrql);
}
VOID CsqReleaseLock(PIO_CSQ IoCsq, KIRQL Irql)
{
KeReleaseSpinLock(SpinLock, Irql);
}
Система передает указатель на переменную IRQL CsqAcquireLock и CsqReleaseLock. Если драйвер использует спин-блокировку для реализации блокировки очереди, драйвер может использовать эту переменную для хранения текущей IRQL при блокировке очереди.
Драйверы не обязаны использовать спинлоки. Например, драйвер может использовать мьютекс для блокировки очереди. Описание методов синхронизации, доступных драйверам, см. в методах синхронизации.
Использование очереди IRP Cancel-Safe
Драйверы используют следующие системные подпрограммы при постановке в очередь и удалении из очереди IRPs:
Один из следующих способов вставки IRP в очередь: IoCsqInsertIrp или IoCsqInsertIrpEx.
IoCsqRemoveNextIrp, чтобы удалить следующий IRP в очереди. Драйвер может дополнительно указать значение ключа.
На следующей схеме показан поток управления для IoCsqRemoveNextIrp.
- IoCsqRemoveIrp чтобы удалить указанный IRP из очереди.
На следующей схеме показан поток управления для IoCsqRemoveIrp.
Эти подпрограммы, в свою очередь, направляются к предоставляемым драйвером подпрограммам.
Подпрограмма IoCsqInsertIrpEx предоставляет доступ к расширенным функциям подпрограммы CsqInsertIrpEx. Он возвращает значение состояния, которое вернул CsqInsertIrpEx. Вызывающий может использовать это значение, чтобы определить, был ли IRP успешно поставлен в очередь или нет. IoCsqInsertIrpEx также позволяет вызывающей стороне указать значение для параметра InsertContext CsqInsertIrpEx.
Обратите внимание, что как IoCsqInsertIrp, так и IoCsqInsertIrpEx можно вызывать в любой безопасной очереди отмены, будь то очередь с функцией CsqInsertIrp или CsqInsertIrpEx. IoCsqInsertIrp ведет себя одинаково в любом случае. Если IoCsqInsertIrpEx передана очередь с подпрограммой CsqInsertIrp, она ведет себя идентично IoCsqInsertIrp.
На следующей схеме показан поток управления для IoCsqInsertIrp.
На следующей схеме показан поток управления для IoCsqInsertIrpEx.
Существует несколько естественных методов использования рутин IoCsqXxx для постановки в очередь и извлечения IRPs. Например, драйвер может просто поставить ИРЗ в очередь для обработки в порядке их получения. Драйвер может поставить IRP в очередь как:
status = IoCsqInsertIrpEx(IoCsq, Irp, NULL, NULL);
Если драйверу не требуется различать определенные IRP, он может просто извлечь их из очереди в порядке, в котором они были поставлены в очередь, как показано ниже.
IoCsqRemoveNextIrp(IoCsq, NULL);
Кроме того, драйвер может ставить в очередь и извлекать из нее определенные запросы ввода-вывода (IRP). Подпрограммы используют непрозрачную структуру IO_CSQ_IRP_CONTEXT для идентификации конкретных IRP в очереди. Драйвер ставит IRP в очередь следующим образом:
IO_CSQ_IRP_CONTEXT ParticularIrpInQueue;
IoCsqInsertIrp(IoCsq, Irp, &ParticularIrpInQueue);
Затем драйвер может удалить одно и то же IRP из очереди, используя значение IO_CSQ_IRP_CONTEXT.
IoCsqRemoveIrp(IoCsq, Irp, &ParticularIrpInQueue);
Драйверу, возможно, потребуется удалить IRP из очереди на основе определённого критерия. Например, драйвер может назначить каждому IRP приоритет таким образом, чтобы IRP с более высоким приоритетом обрабатывались первыми. Драйвер может передать значение PeekContext в IoCsqRemoveNextIrp, который затем система возвращает драйверу, когда он запрашивает следующий IRP в очереди.