Использование подпрограммы «IoTimer»

Хотя таймер для связанного объекта устройства включен, подпрограмма IoTimer вызывается примерно один раз в секунду. Тем не менее, поскольку интервалы, с которыми вызывается каждая подпрограмма IoTimer, зависят от разрешения системных часов, не предполагайте, что подпрограмма IoTimer будет вызываться ровно на границе одной секунды.

Примечаниеподпрограмма IoTimer, как и все подпрограммы DPC, вызывается при IRQL = DISPATCH_LEVEL. Хотя подпрограмма DPC выполняется, все потоки не могут работать на одном процессоре. Разработчики драйверов должны тщательно проектировать свои IoTimer подпрограммы, чтобы они выполнялись как можно быстрее.

Возможно, наиболее распространенное использование для процедуры IoTimer заключается в истечении времени ожидания операций ввода-вывода устройства для IRP. Рассмотрим следующий сценарий использования IoTimer в качестве работающего таймера в драйвере устройства:

  1. При запуске устройства драйвер инициализирует счетчик таймера в расширении устройства на -1, указывая на отсутствие текущих операций ввода-вывода устройства, и вызывает IoStartTimer непосредственно перед возвратом STATUS_SUCCESS.

    Каждый раз при вызове подпрограммы IoTimer проверяет, равен ли счетчик таймера -1, а если да, возвращает элемент управления.

  2. Программа StartIo драйвера инициализирует таймерный счетчик в расширении устройства до верхнего предела, добавляя ещё одну секунду, если рутинная процедура IoTimer была недавно запущена. Затем он использует KeSynchronizeExecution для вызова подпрограммы SynchCritSection_1, которая программирует физическое устройство для операции, запрошенной текущим IRP.

  3. ISR драйвера сбрасывает счетчик таймера на -1 перед добавлением драйвера DpcForIsr или выполнения подпрограммы CustomDpc.

  4. Каждый раз при вызове подпрограммы IoTimer проверяет, был ли счетчик таймера сброшен ISR на -1 и, если да, возвращает управление. Если нет, подпрограмма IoTimer использует KeSynchronizeExecution для вызова подпрограммы SynchCritSection_2, которая настраивает счетчик таймера на некоторое количество секунд, определенное драйвером.

  5. Подпрограмма SynchCritSection_2 возвращает TRUE в подпрограмму IoTimer, пока текущий запрос еще не истек. Если счетчик таймера переходит к нулю, SynchCritSection_2 подпрограмма сбрасывает счетчик таймера в значение времени ожидания сброса драйвера, задает для себя флаг сброса (и для DpcForIsr) в области контекста, пытается сбросить устройство и возвращает TRUE.

    Подпрограмма SynchCritSection_2 будет вызываться снова, если время ожидания операции сброса на устройстве также истекает, и она возвращает FALSE. Если сброс выполнен успешно, подпрограмма DpcForIsr определяет, что устройство было сброшено на основе флага ожидания сброса и повторно выполняет запрос, повторяя действия подпрограммы StartIo, как описано на шаге 2.

  6. Если рутинная процедура SynchCritSection_2 возвращает FALSE, эта процедура IoTimer предполагает, что физическое устройство находится в неизвестном состоянии, поскольку попытка его сбросить уже не удалась. В этих обстоятельствах подпрограмма IoTimer ставит в очередь CustomDpc и завершает выполнение. Эта CustomDpc регистрирует ошибку ввода-вывода устройства, вызывает IoStartNextPacket, сбрасывает текущий IRP и завершает выполнение.

Если ISR драйвера устройства сбрасывает счетчик общего таймера на -1, как описано на шаге 3, его программа DpcForIsr завершает обработку ввода-вывода, управляемую прерываниями, для текущего IRP. Счетчик таймера сброса указывает, что эта операция ввода-вывода устройства не истекла, поэтому подпрограмма IoTimer не требует изменения счетчика таймера.

В большинстве случаев предыдущая SynchCritSection_2 подпрограмма просто уменьшает счетчик таймера. Подпрограмма SynchCritSection_2 пытается сбросить устройство только в том случае, если текущая операция ввода-вывода превысила время ожидания, что указывает момент, когда счетчик таймера достигает нуля. И только если попытка сброса устройства уже завершилась ошибкой, подпрограмма SynchCritSection_2 возвращает FALSE в подпрограмму IoTimer.

Следовательно, как предыдущая IoTimer рутина, так и её вспомогательная SynchCritSection_2 рутина занимают очень мало времени на выполнение в обычных обстоятельствах. Используя процедуру IoTimer таким образом, драйвер устройства гарантирует, что каждый допустимый запрос ввода-вывода устройства может быть повторен при необходимости, и что подпрограмма CustomDpc завершит IRP ошибкой только в том случае, если неустранимая неисправность оборудования предотвращает удовлетворение IRP. Кроме того, драйвер предоставляет эту функцию с очень малой стоимостью во время выполнения.

Простота предыдущего сценария зависит от устройства, которое выполняет только одну операцию за раз и на драйвере, который обычно не перекрывает операции ввода-вывода. Драйвер, выполняющий одновременные операции ввода-вывода устройства, или драйвер более высокого уровня, использующий IoTimer для управления временем ожидания набора IRP, выделенных драйвером и отправленных нескольким цепочкам более низких драйверов, будет иметь более сложные сценарии времени ожидания для управления.