Отслеживание активности прерываний и DPC
Для отслеживания активности прерываний и DPC можно воспользоваться средством ProcessExplorer, открыв диалоговое окно SystemInformation (Системная информация) и перейдя во вкладку CPU (Центральный процессор), где будет показано количество прерываний и DPC-процедур, зафиксированное при каждом обновлении средством Process Explorer отображаемых результатов (по умолчанию с периодичностью в одну секунду).
Можно также отследить выполнение конкретных процедур обслуживания прерываний и отложенных вызовов процедур, используя встроенное отслеживание событий:
- Запустите перехват событий. Откройте окно командной строки, перейдите в каталог Microsoft Windows Performance Toolkit (обычно он находится в каталоге c:\Program Files) и наберите следующую команду1: xperf –on PROC_THREAD+LOADER+DPC+INTERRUPT
- Остановите перехват событий, набрав следующую команду: xperf –d dpcisr.etl
- Сгенерируйте отчеты для перехвата событий, набрав следующее: xperf dpcisr.etl tracerpt \kernel.etl –report dpcisr.html –f html В результате будет сгенерирована веб-страница dpcisr.html.
- Откройте файл report.html и раскройте подраздел DPC/ISR. Раскройте область DPC/ISR Breakdown и увидите сводку, показывающую время, затраченное на ISR-процедуры и DPC-вызовы каждым драйвером. Например, как на рисунке.
При запуске в отладчике ядра команды ln с указанием адреса каждой записи о событии показывается имя функции, выполняющей DPC или ISR:
lkd> ln 0x806321C7
(806321c7) ndis!ndisInterruptDpc
lkd> ln 0x820AED3F
(820aed3f) nt!IopTimerDispatch
lkd> ln 0x82051312
(82051312) nt!PpmPerfIdleDpc
Первым показан DPC-вызов, помещенный в очередь драйвером мини-порта сетевой карты NDIS. Вторым показан DPC-вызов обработки истечения времени общего таймера ввода-вывода. Третий адрес относится к DPC-вызову, предназначенному для выполнения операции простоя (idle).
Кроме использования этого средства для получения отчета в формате HTML, для просмотра подробного обзора всех DPC- ISR-событий можно воспользоваться средством Xperf Viewer, щелкнув в главном окне Xperf правой кнопкой мыши на пункте DPC and/or ISR CPU Usage graphs (Диаграммы использования центрального процессора DPC и ISR) и выбрав пункт Summary Table (Сводная таблица).
Вам будет дана возможность детально рассмотреть каждые DPC и ISR каждого драйвера, а также увидеть продолжительность и количество, как показано на следующем рисунке.
Механизм потоковых DPC-вызовов включен по умолчанию, но его можно отключить путем добавления нулевого DWORD-значения в параметр HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SessionManager\kernel\ThreadDpcEnable. Поскольку потоковые DPC-вызовы могут быть выключены, разработчики драйверов, пользующиеся потоковыми DPC-вызовами, должны писать свои процедуры, следуя тем же правилам, которые распространяются на непотоковые вызовы.
Эти процедуры не могут обращаться к выгружаемой памяти, ждать диспетчеризации или выстраивать предположения насчет IRQL-уровня, с которым они выполняются. Кроме того, они не должны использовать API-функции KeAcquire/ReleaseSpinLockAtDpcLevel, поскольку функции предполагают, что центральный процессор имеет уровень dispatch. Вместо этого в потоковых DPC-вызовах нужно использовать функцию KeAcquire/ReleaseSpinLockForDpc, выполняющую соответствующее действие после проверки текущего IRQL.
Прерывания асинхронных вызовов процедур. Асинхронные вызовы процедур (Asynchronous procedure call, APC) дают пользовательским программам и системному коду способ выполнения в контексте конкретного пользовательского потока (а следовательно, в адресном пространстве конкретного процесса).
Поскольку APC-вызовы выстраиваются в очередь на выполнение в контексте конкретного потока и запускаются на IRQL-уровне, который ниже уровня DPC/dispatch, они не подпадают под такие же ограничения, которые накладываются на DPC. APC-процедура может получать ресурсы (объекты), ждать дескрипторов объектов, справляться с ошибками отсутствия страницы и вызывать системные службы.
APC-вызовы описываются объектом управления ядра, называемым APC-объектом. APC-вызовы, ожидающие выполнения, помещаются в управляемую ядром APC-очередь. В отличие от очереди DPC, которая видна всей системе, APC-очередь относится к конкретному потоку — у каждого потока есть своя собственная APC-очередь. При запросе на помещение в очередь APC-вызова ядро вставляет ее в очередь, принадлежащую тому потоку, который будет выполнять APC-процедуру.
Ядро, в свою очередь, запрашивает программное прерывание на APC-уровне а затем, когда поток через некоторое время начнет работать, в нем выполняется APC-вызов.
Существует два вида APC-вызовов: режима ядра и пользовательского режима. APC-вызовы режима ядра не требуют разрешения от целевого потока на запуск в его контексте, а APC-вызовы пользовательского потока требуют такого разрешения. APC-вызовы режима ядра прерывают поток и выполняются без его вмешательства или разрешения. Есть также два вида APC-вызовов режима ядра: обычные и специальные. Специальные APC-вызовы выполняются на уровне APC и позволяют APC-процедуре изменять некоторые APC-параметры.
Обычные APC-вызовы выполняются на уровне passive и получают измененные параметры от специальной APC-процедуры (или исходные параметры, если они не были изменены).
Обычные и специальные APC-вызовы могут быть отключены путем повышения IRQL на APC-уровень или путем вызова процедуры KeEnterGuardedRegion.
Она отключает APC-доставку, устанавливая поле SpecialApcDisable в структуре KTHREAD вызывающего потока.
Поток может отключить обычные APC-вызовы только путем вызова процедуры KeEnterCriticalRegion, которая устанавливает поле KernelApcDisable в структуре KTHREAD потока. В таблице сведено поведение каждого вида APC-вызова по вставке и доставке APC.
Вставка и доставка APC.
Вид APC-вызова | Поведение, связанное со вставкой | Поведение, связанное с доставкой |
---|---|---|
Специальный (режим ядра) | Вставляется в конец списка APC-вызовов режима ядра | Доставляется на APC-уровне, как только понизится IRQL, и при условии, что поток не находится в защищенной области. Даются указатели на аргументы, определенные при вставке APC-вызова |
Обычный (режим ядра) | Вставляется сразу же после последнего специального APC-вызова (во главе всех остальных обычных APC-вызовов) | Доставляется на уровне PASSIVE_LEVEL после выполнения связанного специального APC-вызова. Доставке задаются аргументы, возвращенные связанным специальным APC- вызовом (это могут быть исходные аргументы, использованные при вставке, или новые аргументы) |
Обычный (пользовательский режим) | Вставляется в конец списка APC-вызовов пользовательского режима | Доставляется на уровне PASSIVE_LEVEL, как только понизится IRQL, и при условии, что поток не находится в критической (или защищенной) области, а также если поток находится в состоянии готовности. Доставке задаются аргументы, возвращенные связанным специальным APC-вызовом (это могут быть исходные аргументы, использованные при вставке, или новые аргументы) |
Обычный (пользовательский режим) Выход из потока(PsExitSpecialApc) | Вставляется в начало списка APC-вызовов пользовательского режима | Доставляется на уровне PASSIVE_LEVEL по возвращении в пользовательский режим, если находится в готовности к выполнению в состоянии ожидания. Доставке задаются аргументы, возвращенные специальным APC- вызовом, завершающим поток |
Исполняющая система использует APC-вызовы режима ядра для выполнения работы операционной системы, которая должна быть завершена в адресном пространстве (в контексте) конкретного потока. Она может использовать специальные APC-вызовы, чтобы направить поток, к примеру, на остановку выполнения прерываемой системной службы или для записи результатов асинхронной операции ввода-вывода в адресном пространстве потока.
Подсистемы среды окружения используют специальные APC-вызовы режима ядра, чтобы заставить поток приостановить или завершить свою работу, или же получить или установить контекст его выполнения в пользовательском режиме. Подсистема для UNIX-приложений использует APC-вызовы режима ядра для имитации доставки UNIX-сигналов процессам подсистемы для UNIX-приложений.
Другое важное применение APC-вызовов режима ядра относится к приостановке или завершению потока. Поскольку эти операции могут инициироваться произвольными потоками и направлены на другие произвольные потоки, ядро использует APC для запроса контекста потока, а также для завершения потока.
Драйверы устройств часто блокируют APC-вызовы или входят в критическую или охраняемую область, чтобы воспрепятствовать выполнению этих операций в тот момент, когда они удерживают блокировку, в противном случае блокировка может быть никогда не снята, и система зависнет.
APC-вызовы режима ядра используются также драйверами устройств.
Например, если инициирована операция ввода-вывода и поток перешел в режим ожидания, может быть спланирован запуск другого потока в другом процессе.
Когда устройство завершит передачу данных, система ввода-вывода должна каким-то образом вернуться в контекст потока, инициировавшего ввод-вывод, чтобы он мог скопировать результаты операции ввода-вывода в буфер в адресном пространстве процесса, содержащего этот поток. Для выполнения этого действия система ввода-вывода использует специальный APC-вызов режима ядра, если только приложение не использовало API-функция SetFileIoOverlappedRange или порты завершения ввода-вывода, — в таком случае либо буфер в памяти будет глобальным, либо копирование его произойдет только после того, как поток извлечет из порта признак завершения.
APC-вызовы пользовательского режима используются несколькими Windows API-функциями, такими как ReadFileEx, WriteFileEx и QueueUserAPC. Например, функции ReadFileEx и WriteFileEx позволяют вызывающему коду указать подпрограмму завершения, вызываемую при окончании операции ввода-вывода.
Завершение ввода-вывода реализуется постановкой APC-вызова в очередь того потока, который выдал запрос на ввод-вывод. Но обратный вызов процедуры завершения не обязательно происходит при постановке APC-вызова в очередь, поскольку APC-вызовы пользовательского режима доставляются потоку только в том случае, когда он находится в готовности к работе в режиме ожидания.
Поток может войти в режим ожидания либо в ожидании дескриптора объекта и обозначении, что его ожидание ведется в готовности к работе (с помощью Windows-функции WaitForMulti pleObjectsEx), либо путем непосредственной проверки на наличие отложенного APC (с помощью функции SleepEx). В обоих случаях, если APC-вызов пользовательского режима отложен, ядро прерывает (извещает) поток, передавая управление APC-процедуре, и возобновляет выполнение потока, когда APC-процедура завершит свою работу. В отличие от APC-вызовов режима ядра, которые могут выполняться на уровне APC, APC-вызовы пользовательского режима выполняются на уровне passive.
Доставка APC может изменить порядок очередей ожидания, то есть изменить списки, в которых указано, какие потоки, что и в каком порядке ожидают. Если при доставке APC-вызова поток находится в состоянии ожидания, после завершения APC-процедуры ожидание повторно выставляется или выполняется.
Если ожидание все еще не разрешено, поток возвращается в состояние ожидания, но теперь он будет в конце списка в отношении тех объектов, которые им ожидаются. Например, поскольку APC-вызовы используются для приостановки выполнения потока, если поток ожидает каких-нибудь объектов, его ожидание удаляется до тех пор, пока не возобновится выполнение потока, после чего этот поток будет в конце списка потоков, ожидающих доступа к объектам, которых он ждет. Поток, выполняющий ожидание в готовности к работе в режиме ядра, будет также разбужен при завершении своей работы. Это позволит такому потоку проверить, разбужен ли он в результате завершения своей работы или по какой-то другой причине.