Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ДП.docx
Скачиваний:
11
Добавлен:
23.09.2019
Размер:
4.64 Mб
Скачать

3.2.2.2 Программные проблемы

Поскольку драйвер работает в режиме ядра, для него весьма несложной задачей является «обрушение» всей операционной системы. Наиболее сложными для трас­сирования сценариями являются операции DMA, в которых некорректно установ­лены регистры отображения (mapping registers). Данные записываются устройст­вом в случайные области памяти, и происходит сбой, виноватыми в котором ка­жутся совершенно другие подсистемы.

Утечка ресурсов. Операционная система никогда не контролирует, как драйвер использует ресур­сы и как он их возвращает по окончании работы. Когда драйвер завершает работу и выгружается, то именно на нем лежит вся ответственность за освобождение всех когда-либо занятых им ресурсов. Утечка памяти может происходить и в то время, когда драйвер еще продолжает работать. Например, если он периодически произ­водит временное выделение памяти под свои нужды и «забывает» их освободить. Драйверы, которые занимаются генерацией дополнительных пакетов IRP, могут «забывать» выполнить очистку этих областей памяти. Утечки ресурсов приводят к падению производительности системы и, в конечном счете, к ее фатальному сбою [3.5].

Поиск утечки ресурсов и в пользовательском режиме является непростой зада­чей, а в режиме ядра он граничит с магией. Возможно, здесь может оказаться полез­ной методика выделения блоков памяти, помеченных дескрипторами, при помо­щи вызова ExAllocatePoolWithTag. При анализе системной памяти после фаталь­ного сбоя, по сохраненному на диске состоянию оперативной памяти, эти идентификаторы позволяют определить, где блоки были выделены, каков их размер и, что самое важное, какая подсистема выполнила их выделение.

Торможение программных потоков. Одна из ошибок, приводящих к остановке программных потоков, состоит в том, что драйвер не завершает обработку IRP пакетов. В результате, поток пользовательского приложения, сделавший запрос, так и остается в состоянии ожидания. Это может быть обусловлено несколькими причинами [3.5].

Самая простая ошибка состоит в том, что драйвер по какой-то причине не выполняет вызов IoCompleteRequest. В результате, присланный IRP пакет никогда не возвращается Диспетчеру ввода/вывода. Иногда не столь очевидна необходимость сделать вызов IoStartNextPacket (при использовании очередей IRP пакетов). Одна­ко даже если не существует запросов, ожидающих обработки, драйвер должен вы­полнить этот вызов, поскольку только таким образом объект устройства будет «по­мечен» как простаивающий. Без этого вызова новые IRP пакеты будут помещаться в очередь ожидания обработки, так и не поступая в процедуру StartIo драйвера.

Другая логическая ошибка, состоящая в попытке рекурсивно получить объект синхронизации или другие ресурсы исполнительной подсистемы, может остано­вить программный поток драйвера в одной из его рабочих процедур. Возможно, что другой фрагмент кода должен был освободить объект синхронизации, но так и не смог «добраться» до нужного места. Последую­щие запросы только усугубляют ситуацию [3.5].

Аналогично, DMA драйверы могут остановиться в точке, где они сделали запрос на владение объектом адаптера. Драйверы, которые управляют сложными (состав­ными) контроллерами, могут создать сходные проблемы в случае, если они не ос­вобождают объект контроллера [3.5].

К сожалению, не существует абсолютно точного и эффективного способа решения таких про­блем. Иногда полезно определить некую структуру данных, в которой все владель­цы объектов синхронизации будут «отмечаться», когда они что-то используют, и удалять эти отметки, когда использование соответствующего объекта синхрони­зации прекращается. Разумеется, такой прием потребует дополнительных усилий, но он весьма эффективен в выявлении источника проблем.

Порой ошибки драйвера могут вызвать блокировку всей системы. Например, фатальную взаимоблокировку могут вызвать некорректное перекрестное использование нескольких объектов спин-блокировок или попытки повторно получить спин-блокировку на однопроцессорной платформе. Бесконечные циклы из кода процедур обслуживания прерывания или процедур DpcForlsr могут привести к аналогичному результату. Лучшим решением в данной ситуации было бы осуществление интерактивной отладки драйвера.

Проблема приоритетов времени выполнения. Поскольку приоритет вызывающего кода по умолча­нию переходит на вызываемые процедуры (которые, возможно, не должны нико­гда работать на этом уровне), то операционная система тщательно отслеживает, чтобы уровень приоритета, который «передался» ее компонентам, соответствовал бы установленным правилам. Наиболее часто жертвой столь ревностного соблюдения правил становится высокоприоритетный код, обращающийся к страничной памяти, которая оказалась сброшенной на диск. Система не позволяет вступать в работу своим функциям, чтобы обслужить данное затруднение, поскольку отсту­пление от данного правила считается вредным для системы [3.5].

Разработчик драйвера должен всегда следить за приоритетом текущего программного кода и документированным уровнем IRQL вызываемых системных функций. В противном случае возможен (а иногда — просто неминуем) крах операционной системы, а ответ придется искать дампе.