Поделиться через


Использование процедуры 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, отправляемых в несколько цепочки более низких драйверов, будет иметь более сложные сценарии времени ожидания.