В процессе работы с операционной системой Windows, пользователь может столкнуться с ощутимым снижением производительности рабочей станции. Естественно первое, что приходит на ум, это воспользоваться встроенным диагностическим средством под названием Диспетчер задач, при вызове которого на проблемной станции можно лицезреть следующую картину:
Очевидно, что по какой то причине в системе вдруг появилась некая, пока еще неопознанная, нагрузка, "съедающая" 20-90% процессорного времени. При всем этом, красный цвет шкалы в окне Загрузка ЦП указывает на нагрузку, вызванную каким-то источником в ядре, иначе кодом, работающим в режиме ядра. Знакомая картина, не правда ли? С одной стороны, похожие проблемы с загрузкой ЦП уже освещались в статье про нагрузку на процессор. Однако, описываемый в данном материале подвид инцидентов стоит выделить в особую категорию, поскольку его отличают ряд особенностей, о которых у нас сейчас и пойдет речь. Давайте для дальнейшей диагностики используем системную утилиту под названием Монитор ресурсов, который даст нам очень интересный результат:
Проблема начинает явно обособляться, и у нас четко просматривается виновник возникающей нагрузки: системные прерывания, которые включают в себя отложенные вызовы процедур (DPC) и процедуры обработки прерываний (ISR). С некоторыми оговорками можно говорить о такой явлении, как шторм прерываний.
Аппаратная часть: теория
Ну и коль скоро мы упомянули о шторме прерываний, не лишним было бы рассказать и о самих прерываниях. Архитектура x86-64 спроектирована таким образом, что в ней присутствует механизм под названием прерывания. Собственно из самого понятия "прерывание" становится очевидным, что что-то где-то чего-то прерывает с какой-то определенной целью.
Со временем развития архитектуры персональных компьютеров, механизм прерываний так же претерпевал существенные изменения. И на данный момент события прерывания выполнения процессором классифицируются следующим образом:
- асинхронные (внешние, аппаратные) - называемые прерываниями - события, инициируемые в любой произвольный момент внешними аппаратными устройствами: сигнал таймера, видеокарты, сетевого адаптера или дискового накопителя, нажатие клавиш клавиатуры, движение мыши. Факт возникновения в системе подобного прерывания расценивается как запрос на прерывание текущей работы (Interrupt request, IRQ) - устройства сообщают, что они требуют внимания со стороны операционной системы;
- синхронные (внутренние, программные) - называемые исключениями:
- сбои (faults) - события, возникающие в центральном процессоре, как результат выполнения ряда условий в ходе исполнения машинного кода: деление на ноль, переполнение стека, недопустимый код операции, обращение к недопустимым адресам (областям) памяти и др.;
- ловушки (traps) - события, инициируемые при выполнении на процессоре специальной инструкции в коде программы (int/syscall/sysenter). Программные прерывания, как правило, используются для обращения к функциям встроенного программного обеспечения (UEFI/BIOS, firmware), драйверов и операционной системы.
Если рассматривать аппаратные прерывания, то они были разработаны с целью оптимизировать архитектуру по скорости обработки запросов от устройств, подключенных к общей системной шине. Как вы знаете, устройства компьютера находятся в постоянной работе: на сетевую карту приходят пакеты данных, непрерывно генерируются сигналы от таймера, пользователь время от времени производит нажатие клавиш клавиатуры/мыши, а так же происходит множество других событий. Так вот, дабы процессор не тратил своё драгоценное время на ожидание в циклическом опросе подключенных аппаратных устройств на предмет наличия данных, было решено добавить механизм, позволяющий самим устройствам сообщать/сигнализировать центральному процессору о необходимости прерывания текущей задачи и обработки поступившей информации. При реализации механизма прерываний в определенной степени оптимизируется быстродействие за счет экономии процессорного времени.
Аппаратные прерывания градируются следующим образом:
- Прерывания, инициируемые сигналом на линии (Line-Based Interrupts, LBI) - базируется на наличии у устройства линии (пина) прерывания, на которой выставляется сигнал о необходимости обработки;
- Прерывания, инициируемые сообщениями (Message-Signalled Interrupts, MSI[-X]) - базируется на записи сообщения (блока данных определенного формата) в адресное пространство, отображаемое на адреса локального контроллера прерываний (LAPIC) процессора;
В зависимости от того, какой именно механизм используется для запроса на обслуживание, либо устройство "заземляет" один из своих контактов INT{A/B/C/D}#
("дергает" свою линию прерывания) или же формирует/отсылает сообщение MSI. Сформированный тем или иным образом запрос прерывания обрабатывается определяемыми используемой архитектурой аппаратными средствами, так в архитектуре IBM-PC-совместимых компьютеров используются так называемые контроллеры прерываний (PIC/APIC/LAPIC/IOAPIC), которые физически реализуются на уровне элементной базы (чипсет/процессор) компьютера и обеспечивают обслуживание всех видов запросов на обслуживание (прерываний/сообщений) от аппаратных устройств.
Так же, контроллеры, работающие с традиционной сигнальной схемой (LBI), поддерживают такие режимы как:
- Режим триггера прерывания, срабатывающего по фронту (запускаемого фронтом) сигнала (edge interrupt trigger mode);
- Режим триггера прерывания, срабатывающего по уровню (запускаемого уровнем) сигнала (level interrupt trigger mode);
Старые системы с шиной ISA на материнской плате не поддерживали триггера прерываний по уровню, соответственно PC/XT, PC/AT-совместимые системы должны были быть запрограммированы на фронтальный триггер прерываний. На системах с шиной MCA, устройства использовали триггер прерываний, срабатывающий по уровню, соответственно и контроллер прерываний запрограммирован всегда работать в режиме срабатывания по уровню. В современных системах (с шинами EISA/PCI/PCIe) применяется универсальные регистры контроля фронта/уровня (Edge/Level Control Registers, ELCR), контролирующие режим на каждой линии IRQ. Регистры ELCR программируются кодом BIOS/UEFI на этапе начальной инициализации станции, размещаются по адресам 0x4D0
и 0x4D1
в адресном пространстве ввода-вывода (архитектура x86) и являются 8-битными.
Логика работы
Внешние прерывания ввода-вывода в виде сигнала поступают на одну из линий контроллера прерываний (IRQ0-IRQ15), либо в виде сообщения, записываемого в зарезервированную область памяти. Современные контроллеры комбинируют множество источников в единую линию прерывания, соединенную с процессором (INTR), а так же отслеживают факт передачи сообщения по битовым шаблонам (при записи сообщения в память через шину данных). Таким образом контроллер, получая сигнал/сообщение (IRQx/MSI) от подчиненного устройства, в свою очередь прерывает работу процессора. Выбранный процессор (ядро) передает контроллеру запрос прерывания (Interrupt request, IRQ), в ответ на который контроллер прерываний превращает IRQ в номер прерывания, использует этот номер в качестве индекса во внутренней структуре под названием таблица дескрипторов прерываний и возвращает процессору вектор прерывания.
В процессорах архитектуры x86 таблица векторов прерываний заполняется дескрипторами шлюзов, которые можно (с оговорками) рассматривать в качестве указателей на функции ядра, которые уже должны обеспечивать непосредственную обработку прерываний/исключений. Дескрипторы, в свою очередь, могут быть трех типов: шлюз прерывания, шлюз ловушки и шлюз задачи. В операционной системе Windows существует схожая таблица, которая (не смотря на полную схожесть аббревиатуры) носит название таблицы диспетчеризации прерываний (Interrupt Dispatch Table) и подготавливается ядром на этапе загрузки операционной системы.
Windows отображает аппаратные IRQ-запросы на номера прерываний в IDT и также использует IDT для настройки обработчиков системных прерываний для исключений. Именно на основании данных из IDT процессор производит обработку прерывания, осуществляя передачу управления соответствующей процедуре обработки. Таким образом, операционная система Windows организует диспетчеризацию прерываний.
Содержимое IDT, включая информацию о том, какие обработчики системных прерываний назначены операционной системой Windows прерываниям (включая исключения и IRQ-запросы), можно посмотреть, используя команду !idt отладчика WinDbg.
Общие причины
Давайте попробуем собрать распространенные причины возникновения шторма прерываний на аппаратном и программном уровнях:
- Устройство не освобождает (не снимает) сигнал прерывания с линии (INTx#) по требованию собственного драйвера.
- Драйвер устройства не инициирует запрос на снятие сигнала прерывания (INTx#) с обслуживаемого устройства (EOI?), по причине того, что не определяет момент выставления сигнала прерывания устройством.
- Драйвер устройства заявляет о прерывании в случае, когда прерывание не было инициировано обслуживаемым устройством (случается, когда множество устройств используют одно и то же IRQ).
- Регистр контроля фронта/уровня (ELCR) содержит некорректное значение.
- Устройства, работающие в обеих режимах триггера прерывания (по фронтам и по уровню), разделяют одно и то же IRQ (например, COM-порт и PCI SCSI-контроллер).
- Драйвер контроллера прерываний постоянно сообщает о наличии прерывания от устройства, при этом не производя его обработку. В этом случае создается ситуация, когда прерывания от устройств не обслуживаются. Устройство, выставляющее сигнал "прерывание", не обслуживается и вынуждено вновь выставить прерывание, которое, опять же, не обслуживается, и так в цикле.
- Некоторые системные драйвера репортуют о том, что подчиненные устройства требуют обработки прерывания, в то время как на самом деле данные устройства не выставляют запрос на прерывание. Подобное случае когда устройства на материнской плате работают некорректно.
В общем случае, если исключить все косвенные факторы влияния, всё сводится к двум основным причинам:
- Неисправному (сбоящему) аппаратному устройству;
- Проблемному (глючному) драйверу устройства;
Программная часть: обнаружение источника нагрузки
Для нахождения причины дисфункции может быть использован довольно широкий круг инструментария, тем не менее наиболее приспособлены для данной задачи следующие приложения:
- Проведение диагностики при помощи утилит комплекта Windows Performance Tools, методика частично описана в статье о том, что грузит процессор;
- Проведение локальной/дистанционной отладки ядра при помощи отладчика WinDbg;
- Диагностика при помощи утилиты LatencyMon;
- Диагностика при помощи утилиты DPC Latency Checker;
- Диагностика при помощи утилиты Process Hacker;
Источник: сторонний драйвер
При определении источника шторма прерываний при помощи одного из перечисленных выше средств диагностики, подозрение часто падает на драйвер(а) стороннего производителя, поскольку именно они имеют проблемы со стабильностью кода. Типичный пример выявления драйвера, который ответственен за шторм прерываний при помощи утилиты LatencyMon:
Очевидно что это относительно простой случай. На картинке отчетливо видно имя виновного драйвера, им, в нашем конкретном случае является HECIx64.sys, входящий в комплект драйверов Intel Management Engine Interface. В любом случае, по имени драйвера-источника в Сети Интернет (при помощи поисковика) можно всегда найти полное наименование комплекта драйверов, содержащего в своем составе упоминаемый драйвер. Общая последовательность действий выглядит следующим образом:
- По имени драйвера определить принадлежность к определенному программному комплексу или аппаратному устройству;
- Обновить непосредственно драйвер или программный комплекс, включающий в себя (имеющий в своем составе) проблемный драйвер;
- Проверить работоспособность сопутствующих служб/сервисов, если таковые были установлены вместе с драйвером устройства;
- Отключить/заменить устройство или изменить аппаратную конфигурацию (настройки BIOS/собственной прошивки) подчиненного устройства;
Эти простые действия, в большинстве случаев, приводят к действенным положительным результатам.
Источник: системный драйвер
Но встречаются и ситуации, в которых диагностика инцидента, связанного со штормом прерываний, значительно усложняется. Это происходит, когда виновником является один из системных драйверов (драйверов, входящих в состав операционной системы).
По снимку экрана видно, что в данном случае источником нагрузки на процессор является большое количество вызовов функций системного драйвера acpi.sys. При этом, в некоторых случаях при трассировке в отчетах отмечаются только процедуры верхнего уровня: ACPIInterruptDispatchEventDpc
, ACPIInterruptServiceRoutine
, ACPIDevicePowerDPC
, ACPIInterruptServiceRoutineDPC
, в других случаях удается записать стеки вызовов, которые упираются во взаимодействие кода драйвера acpi.sys с функциями уровня аппаратных абстракций hal.dll, работающими с конфигурационным пространством PCI.
В общем случае, драйвер отвечает за обработку прерываний по событиям, относящимся к Управлению питанием и обработку прерываний от устройств на материнской плате. ACPI это средство, которым BIOS/UEFI сообщает системе, какие устройства имеются в системе, где они расположены, какие используют ресурсы, какие топологии шин подключены, какие возможности по управлению питанием есть в наличии, и так далее.
Стандарт ACPI обеспечивает следующий функционал:
- обеспечение интерфейса к шине и контроллеру (SMBus);
- контроль за потреблением энергии устройств: различные состояния/режимы энергопотребления компонентов;
- мониторинг состояния оборудования: отслеживание температуры, скорости работы вентиляторов и прочих датчиков;
- автоматическое конфигурирование устройств;
- обслуживание системных событий;
Драйвер acpi.sys общается с:
- ACPI-таблицы конфигурации -- блоки определения, описывающие интерфейсы аппаратных средств.
- ACPI-регистры -- часть описания интерфейсов из ACPI-таблиц, организованная в виде регистров (для доступа к устройствам, не поддерживающим PCI пространство конфигурации).
- ACPI-BIOS -- часть кода BIOS/UEFI, которая совместима с ACPI-спецификациями. Как правило это код, отвечающий за загрузку, засыпание/пробуждение и перезагрузку станции.
Помимо того, что ACPI выполняет огромную роль в процессе загрузки компьютера, нужно помнить, что ACPI так же участвует в управлении различными аспектами функционирования оборудования на протяжении всего цикла работы. И осуществляется это при помощи так называемых событий общего назначения (ACPI General Purpose Event, GPE).
- От аппаратных устройств посредством ACPI BIOS поступают события управления питанием (PM Event, Power Management Event), логика в чипсете принимает данные сообщения и выставляет признак события GPE.
- Старые PCI-устройства и устройства, интегрированные в чипсет, посылают уведомление непосредственно через ACPI.
ACPI-таблица содержит пространства, закрепленные за определенными системными ресурсами, и когда аппаратное обеспечение запускает прерывание ACPI, функции ядра считывают значения из этой области, чтобы увидеть, какие GPE помечены. Код ACPI ASL может использовать идентификатор инициатора (источника) для информирования операционной системы об устройстве, вызвавшем событие.
Это все хорошо, однако нам хотелось бы докопаться до причины даже в отсутствии внятных средств диагностики проблемы. Сразу возникает ряд вопросов: через какие механизмы/события/прерывания устройства сигнализируют о состоянии управления питанием? С какими именно аппаратными устройствами работает драйвер acpi.sys? Например, на одной из рабочих машин, при помощи команды !acpiirqarb
(показывает структуру ACPI IRQ арбитра, содержащую конфигурацию устройств ввода-вывода на входах контроллера прерываний и таблицу диспетчеризации прерываний (IDT) выбранного процессора) я получил следующий вывод:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
lkd> !acpiirqarb Processor 0 (0, 0): Device Object: 0000000000000000 Current IDT Allocation: 0000000000000000 - 0000000000000050 00000000 A:0000000000000000 IRQ(GSIV):0 0000000000000051 - 0000000000000051 D fffffa800b561e40 A:0000000000000000 IRQ(GSIV):0 0000000000000060 - 0000000000000060 D fffffa800b566060 (amdkmdap) A:0000000000000000 IRQ(GSIV):0 0000000000000061 - 0000000000000061 D fffffa800b5607e0 A:0000000000000000 IRQ(GSIV):0 0000000000000062 - 0000000000000062 S B fffffa800b545a10 (usbehci) A:0000000000000000 IRQ(GSIV):0 0000000000000070 - 0000000000000070 D fffffa800b544a10 (pci) A:0000000000000000 IRQ(GSIV):0 0000000000000071 - 0000000000000071 D fffffa800b560a00 A:0000000000000000 IRQ(GSIV):0 0000000000000072 - 0000000000000072 S B fffffa800b540a10 (HDAudBus) A:0000000000000000 IRQ(GSIV):0 0000000000000080 - 0000000000000080 D fffffa800b543a10 (pci) A:0000000000000000 IRQ(GSIV):0 0000000000000081 - 0000000000000081 D fffffa800b55f620 (i8042prt) A:0000000000000000 IRQ(GSIV):0 0000000000000082 - 0000000000000082 D fffffa800b53fa10 (e1cexpress) A:0000000000000000 IRQ(GSIV):0 0000000000000090 - 0000000000000090 D fffffa800b542a10 (pci) A:0000000000000000 IRQ(GSIV):0 0000000000000091 - 0000000000000091 D fffffa800b55e620 (i8042prt) A:0000000000000000 IRQ(GSIV):0 0000000000000092 - 0000000000000092 S 0000000000000092 - 0000000000000092 S B fffffa800b53ea10 (MEIx64) A:0000000000000000 IRQ(GSIV):0 0000000000000092 - 0000000000000092 S B fffffa800b540060 (usbehci) A:0000000000000000 IRQ(GSIV):0 00000000000000a0 - 00000000000000a0 D fffffa800b541a10 (pci) A:0000000000000000 IRQ(GSIV):0 00000000000000a1 - 00000000000000a1 D fffffa800b259e30 A:0000000000000000 IRQ(GSIV):0 00000000000000a2 - 00000000000000a2 S 00000000000000a2 - 00000000000000a2 S B fffffa800b567060 (HDAudBus) A:0000000000000000 IRQ(GSIV):0 00000000000000a2 - 00000000000000a2 S B fffffa800b53f060 (Serial) A:0000000000000000 IRQ(GSIV):0 00000000000000b0 - 00000000000000b0 D fffffa800b53e060 (pci) A:0000000000000000 IRQ(GSIV):0 00000000000000b1 - 00000000000000b1 S B fffffa800b253060 (ACPI) A:0000000000000000 IRQ(GSIV):0 00000000000000b2 - 00000000000000b2 S B fffffa800b553060 (iaStor) A:0000000000000000 IRQ(GSIV):0 00000000000000bf - ffffffffffffffff 00000000 A:0000000000000000 IRQ(GSIV):0 . . . |
Ну, похоже на перечисление аппаратных устройств системы. В идеальном случае, хотелось бы наблюдать однозначное соответствие между номером вектора прерывания в таблице IDT и сопоставленной процедурой обработки, по имени которой можно было бы достоверно определить сопоставленное оборудование. Собственно, в выводе команды присутствуют номера векторов прерываний (записей) в таблице IDT (первые две колонки), которые сопоставлены с именем устройства в системе (6-я колонка). Теперь попробуем найти функцию обработки прерывания и для этого воспользуемся командой !idt -a
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
lkd> !idt -a Dumping IDT: fffff80000b93000 * исключения (зарезервированы за процессором) 00: fffff80002fe5100 nt!KiDivideErrorFaultShadow 01: fffff80002fe5180 nt!KiDebugTrapOrFaultShadow Stack = 0xFFFFF80000B969E0 02: fffff80002fe5200 nt!KiNmiInterruptShadowStart Stack = 0xFFFFF80000B967E0 03: fffff80002fe5280 nt!KiBreakpointTrapShadow 04: fffff80002fe5300 nt!KiOverflowTrapShadow 05: fffff80002fe5380 nt!KiBoundFaultShadow 06: fffff80002fe5400 nt!KiInvalidOpcodeFaultShadow 07: fffff80002fe5480 nt!KiNpxNotAvailableFaultShadow 08: fffff80002fe5500 nt!KiDoubleFaultAbortShadow Stack = 0xFFFFF80000B963E0 09: fffff80002fe5580 nt!KiNpxSegmentOverrunAbortShadow 0a: fffff80002fe5600 nt!KiInvalidTssFaultShadow 0b: fffff80002fe5680 nt!KiSegmentNotPresentFaultShadow 0c: fffff80002fe5700 nt!KiStackFaultShadow 0d: fffff80002fe5780 nt!KiGeneralProtectionFaultShadow 0e: fffff80002fe5800 nt!KiPageFaultShadow 0f: fffff80002fe5df8 nt!KiIsrThunkShadow+0x78 10: fffff80002fe5880 nt!KiFloatingErrorFaultShadow 11: fffff80002fe5900 nt!KiAlignmentFaultShadow 12: fffff80002fe5980 nt!KiMcheckAbortShadow Stack = 0xFFFFF80000B965E0 13: fffff80002fe5a00 nt!KiXmmExceptionShadow * программные прерывания 1f: fffff80002fe5a80 nt!KiApcInterruptShadow 2c: fffff80002fe5b00 nt!KiRaiseAssertionShadow 2d: fffff80002fe5b80 nt!KiDebugServiceTrapShadow 2f: fffff80002fe5c00 nt!KiDpcInterruptShadow * аппаратные прерывания 14: fffff80002fe5e20 nt!KiIsrThunkShadow+0xA0 15: fffff80002fe5e28 nt!KiIsrThunkShadow+0xA8 . . . 50: fffff80002fe6000 nt!KiIsrThunkShadow+0x280 . . . 51: fffff80002fe6008 nt!KiIsrThunkShadow+0x288 . . . 60: fffff80002fe6080 nt!KiIsrThunkShadow+0x300 61: fffff80002fe6088 nt!KiIsrThunkShadow+0x308 62: fffff80002fe6090 nt!KiIsrThunkShadow+0x310 . . . 70: fffff80002fe6100 nt!KiIsrThunkShadow+0x380 71: fffff80002fe6108 nt!KiIsrThunkShadow+0x388 72: fffff80002fe6110 nt!KiIsrThunkShadow+0x390 . . . 80: fffff80002fe6180 nt!KiIsrThunkShadow+0x400 81: fffff80002fe6188 nt!KiIsrThunkShadow+0x408 82: fffff80002fe6190 nt!KiIsrThunkShadow+0x410 . . . 90: fffff80002fe6200 nt!KiIsrThunkShadow+0x480 91: fffff80002fe6208 nt!KiIsrThunkShadow+0x488 92: fffff80002fe6210 nt!KiIsrThunkShadow+0x490 . . . a0: fffff80002fe6280 nt!KiIsrThunkShadow+0x500 a1: fffff80002fe6288 nt!KiIsrThunkShadow+0x508 a2: fffff80002fe6290 nt!KiIsrThunkShadow+0x510 . . . b0: fffff80002fe6300 nt!KiIsrThunkShadow+0x580 b1: fffff80002fe6308 nt!KiIsrThunkShadow+0x588 b2: fffff80002fe6310 nt!KiIsrThunkShadow+0x590 . . . bf: fffff80002fe6378 nt!KiIsrThunkShadow+0x5F8 . . . e1: fffff80002fe5c80 nt!KiIpiInterruptShadow . . . |
В первой колонке мы видим номер вектора прерывания, во второй адрес обработчика, в третьей присутствует имя функции-обработчика. Как уже говорилось, в таблице IDT присутствуют дескрипторы-указатели на функции ядра, являющиеся обработчиками прерываний устройств. Но на деле, как можно наблюдать по выводу выше, только часть обработчиков (исключения/программные прерывания) в таблице IDT имеют имена, хоть как-то определяющие назначение функции-обработчика. По именам всех остальных элементов таблицы (обработчиков аппаратных прерываний) сложно определить, с каким именно устройством они взаимосвязаны. Все замаскировано под некую функцию-диспетчер KiIsrThunkShadow
, которая в своем коде (с разным смешением от начала), содержит 256 шаблонов перенаправления вызова. То есть, непосредственно диспетчеризация прерываний ядром Windows представляет собой многоступенчатый процесс, в ходе которого выполняется некоторое количество подготовительных действий перед передачей управления драйверам, зарегистрировавшим процедуры обработки прерываний (ISR).
1 2 3 4 5 |
lkd> !idt 50 Dumping IDT: fffff80000b93000 50: fffff80002fe6000 nt!kiIsrThunkShadow+0x280 |
1 2 3 4 5 6 7 8 9 10 |
lkd> u nt!KiIsrThunkShadow+0x280 nt!KiIsrThunkShadow+0x280: fffff800`02fe6000 6a50 push 50h fffff800`02fe6002 e9b9050000 jmp nt!KxIsrLinkageShadow (fffff800`02fe65c0) fffff800`02fe6007 cc int 3 fffff800`02fe6008 6a51 push 51h fffff800`02fe600a e9b1050000 jmp nt!KxIsrLinkageShadow (fffff800`02fe65c0) fffff800`02fe600f cc int 3 fffff800`02fe6010 6a52 push 52h fffff800`02fe6012 e9a9050000 jmp nt!KxIsrLinkageShadow (fffff800`02fe65c0) |
Так, получается, что в стек мы заталкиваем номер вектора прерывания (например 50h
в строке 3
), после чего функция-диспетчер KiIsrThunkShadow
передает управление к подчиненным функциям KxIsrLinkageShadow
. Сама же функция KxIsrLinkageShadow
представляет собой обертку к KxIsrLinkage
/KiIsrLinkage
, выполняющие предварительные проверочные/настроечные действия базового адреса стека ядра, значения PML4
(адресное пространство ядра) с инициализацией CR3
. Функция KiIsrLinkage
выполняет следующие действия:
- Сохраняет значения используемых регистров в структуре KTRAP_FRAME, созданной в стеке.
- Проверяет есть ли активная обработка прерывания, ЦП выполнял инструкции в пределах определенного региона функции
ExpInterlockedPopEntrySList*
и если это так, то сбрасывает указатель исполняемой инструкции (RIP). - Проверяет, отключены ли прерывания и если это так, то производит останов системы с кодом TRAP_CAUSE_UNKNOWN.
- Регистрирует указатель на структуру прерывания, ассоциированную с прерыванием и производит обслуживание прерывания.
- Восстанавливает значения используемых регистров из структуры KTRAP_FRAME.
- Производит возврат из процедуры обработки прерывания.
Но главное, на что стоит обратить внимание, так это, что внутри функции KiIsrLinkage
каким-то образом определяется адрес обработчика, зарегистрированного тем или иным драйвером устройства. Скорее всего как-то задействована структура KINTERRUPT, однако с кодом я пока не разобрался.
Общие рекомендации
Ну куда же нам деться от хорошо нам знакомых "общих рекомендаций", называемых, так же, "танцами с бубном":
- Проблема связана с Power Management и SATA AHCI драйвером. В случае с материнской платы на чипсете Intel: при наличии установленного Intel Rapid Storage Technolody Driver (RST), откатить драйвер на предыдущую версию. Ссылка для скачивания с официального сайта.
- Проблема с режимами работы шины PCI Express. Открываем Панель управления - Электропитание - опция Настройка плана электропитания для текущего плана - Изменить дополнительные параметры питания - ветвь PCI Express - развернуть Управление питанием состояния связи - установить в значение Откл.
Если нет этой опции, то идем в реестр по пути HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\PowerSettings\501a4d13-42af-4429-9fd1-a8218c268e20\ee12f906-d277-404b-b6da-e5fa1a576df5, создаем параметр с именем Attributes типа DWORD и выставляем значение 0.
- Проблема с сетевым адаптером. Открываем Диспетчер устройств - раздел Сетевые адаптеры - находим установленную сетевую карту - в свойствах открываем вкладку Управление электропитанием - Отключить опции Разрешить этому устройству выводить компьютер из ждущего режима (Wake-on-LAN (WOL)) и Разрешать вывод компьютера из ждущего режима только с помощью "магического пакета" (Disabling magic packets).