Это то, что на самом деле явно не объяснено ни в одном источнике, и что интересно, второй комментарий также задает тот же вопрос.
После изучения ядра ReactOS и WRK я теперь точно знаю, что происходит
Драйвер, когда он получает IRP_MN_START_DEVICE
от диспетчера PnP, инициализирует объект прерывания, используя IoConnectInterrupt
, используя данные в CM_RESOURCE_LIST
, которые он получает в IRP. Особый интерес представляют вектор и сходство, которые были назначены PnP-менеджером устройству (это легко сделать, если устройство предоставляет возможность MSI в своем пространстве конфигурации PCIe, поскольку ему не нужно беспокоиться о базовой маршрутизации IRQ). Он передает вектор, указатель на ISR, контекст для ISR, IRQL на IoConnectInterrupt
, который вызывает KeInitializeInterrupt
для инициализации объекта прерывания с использованием параметров, а затем вызывает KeConnectInterrupt
, который переключает сходство текущего потока на целевой процессор блокирует базу данных диспетчера и проверяет, что эта запись IDT указывает на оболочку BugCheck KxUnexpectedInterrupt0[IdtIndex]
. Если это так, тогда IRQL поднимается до 31, поэтому следующая операция является атомарной и использует HAL API, чтобы включить вектор, который был отображен диспетчером PnP в LAPIC, и назначить ему уровень приоритета TPR, соответствующий IRQL. Затем он отображает вектор на адрес обработчика в записи IDT для вектора. Для этого он передает адрес &Interrupt->DispatchCode[0]
в процедуру отображения IDT KeSetIdtHandlerAddress
. Похоже, что это шаблон , который одинаков для всех объектов прерываний, который согласно WRK равен KiInterruptTemplate
. Конечно же, проверяя ядро ReactOS, мы видим в KeInitializeInterrupt
- который вызывается IoConnectInterrupt
- код:
RtlCopyMemory(Interrupt->DispatchCode,
KiInterruptDispatchTemplate,
sizeof(Interrupt->DispatchCode));
KiInterruptDispatchTemplate
пока пусто, потому что порт ReactOS amd64 находится на ранней стадии разработки. На окнах это будет реализовано как и KiInterruptTemplate
.
Затем IRQL возвращается к старому IRQL. Если запись IDT не указывает на ISR BugCheck, то она инициализирует связанное прерывание - потому что в записи IDT уже был адрес. Он использует CONTAINING_RECORD
для получения объекта прерывания его членом, адресом обработчика (DispatchCode[0]
) и соединяет новый объект прерывания с уже существующим, инициализируя объект прерванного объекта LIST_ENTRY
, на который уже ссылаются, в качестве заголовка перечислите и пометьте его как цепочечное прерывание, установив для члена DispatchAddress
адрес KiChainedDispatch
. Затем он сбрасывает спин-блокировку базы данных диспетчера и переключает обратно сходство и возвращает объект прерывания.
Затем драйвер устанавливает DPC - с DeferredRoutine
в качестве участника - для объекта устройства, используя IoInitializeDpcRequest
.
FORCEINLINE VOID IoInitializeDpcRequest ( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIO_DPC_ROUTINE DpcRoutine )
KeInitializeDpc(&DeviceObject->Dpc,
(PKDEFERRED_ROUTINE) DpcRoutine,
DeviceObject);
KeInitializeDpc
вызывает KiInitializeDpc
, который жестко задан для установки приоритета на средний, что означает, что KeInsertQueueDpc
поместит его в середину очереди DPC. KeSetImportanceDpc
и KeSetTargetProcessorDpc
могут использоваться после вызова для установки возвращенного DPC, для которого был сгенерирован приоритет и целевой процессор соответственно. Он копирует объект DPC в член объекта устройства и, если объект DPC уже существует, ставит его в очередь на уже существующий DPC.
Когда происходит прерывание, шаблон KiInterruptTemplate
объекта прерывания - это адрес в IDT, который вызывается, который затем вызывает реальный диспетчер прерываний , который является членом DispatchAddress
, который будетKiInterruptDispatch
для нормального прерывания или KiChainedDispatch
для цепного прерывания.Он передает объект прерывания в KiInterruptDispatch
(он может сделать это, потому что, как мы видели ранее, RtlCopyMemory
скопировал KiInterruptTemplate
в объект прерывания, это означает, что он может использовать блок asm с относительным RIP для получения адресаобъекта прерывания, которому он принадлежит (он также может пытаться что-то сделать с помощью функции CONTAINING_RECORD
), но intsup.asm
содержит следующий код для этого: lea rbp, KiInterruptTemplate - InDispatchCode ; get interrupt object address jmp qword ptr InDispatchAddress[rbp]; finish in common code
).KiInterruptDispatch
получит спин-блокировку прерывания, вероятно, используя KeAcquireInterruptSpinLock
.ISR (ServiceContext
) вызывает IoRequestDpc
с адресом объекта устройства, который был создан для устройства и ISR, в качестве параметра вместе с конкретным контекстом прерывания и необязательным IRP (который, я предполагаю, он получает из головы вDeviceObject->Irp
, если подпрограмма предназначена для обработки IRP).Я ожидал, что это будет однострочная оболочка KeInsertQueue
, но вместо этого передается член Dpc объекта устройства, и это именно то, что и есть: KeInsertQueueDpc(&DeviceObject->Dpc, Irp, Context);
.Во-первых, KeInsertQueue
поднимает IRQL с устройства IRQL устройства ISR до 31, что предотвращает любое прерывание. WRK содержит следующую строку в строке 263 из dpcobj.c
:
#if !defined(NT_UP)
if (Dpc->Number >= MAXIMUM_PROCESSORS) {
Number = Dpc->Number - MAXIMUM_PROCESSORS;
TargetPrcb = KiProcessorBlock[Number];
} else {
Number = CurrentPrcb->Number;
TargetPrcb = CurrentPrcb;
}
Что говорит о том, что элемент DPC->Number
должен быть установлен в KeSetTargetProcessorDpc
как целевое число ядер + максимальное количество процессоров.Это странно и, конечно же, я пошел и посмотрел на ReactOS KeSetTargetProcessorDpc
, и это так!KiProcessorBlock
представляется структурой ядра для быстрого доступа к структурам KPRCB для каждого из ядер.
Затем он получает обычную спин-блокировку очереди DPC ядра, используя DpcData = KiSelectDpcData(TargetPrcb, Dpc)
, который возвращает &Prcb->DpcData[DPC_NORMAL]
в качестве типаDPC, который он передал, является нормальным, а не резьбовым.Затем он получает спин-блокировку для очереди, и это выглядит как пустое тело функции в ReactOS, и я думаю, что это из-за этого:
/ * В сборках UP спин-блокировки не существуют в IRQL>= DISPATCH * /
И это имеет смысл, потому что ReactOS поддерживает только одно ядро, то есть на другом ядре нет потока, который мог бы получить доступ к очереди DPC (ядро может иметь целевой DPC для очереди этого ядра),Существует только одна очередь DPC.Если бы это была многоядерная система, она должна была бы получить спин-блокировку, чтобы они выглядели как заполнители для реализации многоядерной функциональности.Если ему не удалось получить спин-блокировку для очереди DPC, то он либо ожидал бы вращение на IRQL 31, либо перешел на IRQL самого прерывания и ожидал, что позволило бы другим прерываниям происходить с ядром, но другие потоки не могли работать на ядре..
Обратите внимание, что Windows использует KeAcquireSpinLockAtDpcLevel
для получения этого спинлока, а ReactOS - нет.KeAcquireSpinLockAtDpcLevel
не касается IRQL .Хотя в WRK он напрямую использует KiAcquireSpinLock
, который можно увидеть в строке 275 из dpcobj.c
, которая только получает спин-блокировку и ничего не делает с IRQL (KiAcquireSpinLock(&DpcData->DpcLock);
).
После получения спин-блокировки онаво-первых, гарантирует, что объект DPC еще не находится в очереди (элемент DpcData
будет иметь значение NULL, если он cmpxchg
инициализирует его с помощью DpcData
, возвращенного из KiSelectDpcData(TargetPrcb, Dpc)
), и если это так, он удаляет спин-блокировкуи возвращается;в противном случае он устанавливает членов DPC так, чтобы они указывали на определенный контекст прерывания, который был передан, а затем вставляет его в очередь либо в начале (InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
), либо в хвосте (InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
) в зависимости от приоритета (* 1108).*).Затем он проверяет, что DPC не выполняет уже if (!(Prcb->DpcRoutineActive) && !(Prcb->DpcInterruptRequested))
.Затем он проверяет, вернул ли KiSelectDpcData
вторую структуру KDPC_DATA
, т. Е. DPC имел тип с резьбой (if (DpcData == &TargetPrcb->DpcData[DPC_THREADED])
), а если это и if ((TargetPrcb->DpcThreadActive == FALSE) && (TargetPrcb->DpcThreadRequested == FALSE))
, то он блокирует xchg
, чтобы установить TargetPrcb->DpcSetEventRequest
в значение соответственноа затем он устанавливает TargetPrcb->DpcThreadRequested
и TargetPrcb->QuantumEnd
в значение true и устанавливает RequestInterrupt
в значение true, если целевой PRCB является текущим PRCB, в противном случае он устанавливает его в значение true, только если целевое ядро не находится в режиме ожидания.
Теперь суть первоначального вопроса.WRK теперь содержит следующий код:
#if !defined(NT_UP)
if (CurrentPrcb != TargetPrcb) {
if (((Dpc->Importance == HighImportance) ||
(DpcData->DpcQueueDepth >= TargetPrcb->MaximumDpcQueueDepth))) {
if (((KiIdleSummary & AFFINITY_MASK(Number)) == 0) ||
(KeIsIdleHaltSet(TargetPrcb, Number) != FALSE)) {
TargetPrcb->DpcInterruptRequested = TRUE;
RequestInterrupt = TRUE;
}
}
} else {
if ((Dpc->Importance != LowImportance) ||
(DpcData->DpcQueueDepth >= TargetPrcb->MaximumDpcQueueDepth) ||
(TargetPrcb->DpcRequestRate < TargetPrcb->MinimumDpcRate)) {
TargetPrcb->DpcInterruptRequested = TRUE;
RequestInterrupt = TRUE;
}
}
#endif
По сути, в многопроцессорной системе, если целевое ядро, которое оно получило от объекта DPC, не является текущим ядром потока, то: если DPC имеет большое значение или превышает максимальную глубину очереди и логическое значение and
целевого сродства и незанятых ядер равно 0 (т. е. целевое ядро не бездействует) и (ну, KeIsIdleHaltSet
, по-видимому, точно то же самое (он проверяет флаг Sleeping в целевом PRCB)), тогда он устанавливает флаг DpcInterruptRequested
в PRCB целевого ядра . Если целью DPC является текущее ядро, то если DPC не имеет низкого значения (примечание: это позволило бы использовать среду!) Или если глубина очереди DPC превышает максимальную глубину очереди, и если частота запросов DPC на ядре не имеет t превысил минимум, он устанавливает флаг в PRCB текущего ядра , чтобы указать, что есть DPC.
Теперь он освобождает спин-блокировку очереди DPC: KiReleaseSpinLock(&DpcData->DpcLock);
(конечно, #if !defined(NT_UP)
) (которая не изменяет IRQL). Затем он проверяет, запрашивалось ли прерывание процедурой (if (RequestInterrupt == TRUE)
), затем, если это однопроцессорная система (#if defined(NT_UP)
), он просто вызывает KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
, но если это многоядерная система, ему необходимо проверить цель PRCB, чтобы увидеть, нужно ли отправлять IPI.
if (TargetPrcb != CurrentPrcb) {
KiSendSoftwareInterrupt(AFFINITY_MASK(Number), DISPATCH_LEVEL);
} else {
KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
}
И это говорит само за себя, что это делает; если текущий PRCB не является целевым PRCB DPC, то он отправляет IPI с приоритетом DISPATCH_LEVEL
на номер процессора, используя KiSendSoftwareInterrupt
; в противном случае он использует KiRequestSoftwareInterrupt
. Документации вообще нет, но я предполагаю, что это Self IPI, и он обернет функцию HAL, которая запрограммирует ICR для отправки IPI себе с приоритетом уровня диспетчеризации (на этом этапе я считаю, что ReactOS вызывает HalRequestSoftwareInterrupt
, который показывает невыполненную запись PIC). Так что это не программное прерывание в смысле INT
, а на самом деле, просто аппаратное прерывание. Затем он понижает IRQL с 31 до предыдущего IRQL (который был ISR IRQL). Затем он возвращается к ISR, а затем возвращается к KiInterruptDispatch
; KiInterruptDispatch
затем освободит спин-блокировку ISR, используя KeReleaseInterruptSpinLock
, которая уменьшит IRQL до того, что было до прерывания, и затем выскочит кадр прерывания, но я бы подумал, что сначала он выдвинет кадр прерывания, а затем запрограммирует LAPIC TPR. поэтому процесс восстановления регистра является атомарным, но я полагаю, это не имеет значения.
ReactOS имеет следующее (WRK не имеет KeReleaseSpinlock
или процедуры снижения IRQL задокументированы, так что это лучшее, что у нас есть):
VOID NTAPI KeReleaseSpinLock ( KIRQL NewIrql )
{
/* Release the lock and lower IRQL back */
KxReleaseSpinLock(SpinLock);
KeLowerIrql(OldIrql);
}
VOID FASTCALL KfReleaseSpinLock ( PKSPIN_LOCK SpinLock, KIRQL OldIrql )
{
/* Simply lower IRQL back */
KeLowerIrql(OldIrql);
}
KeLowerIrql - это оболочка для функции HAL KfLowerIrql, функция содержит KfLowerIrql(OldIrql);
и все.
VOID FASTCALL KfLowerIrql ( KIRQL NewIrql )
{
DPRINT("KfLowerIrql(NewIrql %d)\n", NewIrql);
if (NewIrql > KeGetPcr()->Irql)
{
DbgPrint ("(%s:%d) NewIrql %x CurrentIrql %x\n",
__FILE__, __LINE__, NewIrql, KeGetPcr()->Irql);
KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
for(;;);
}
HalpLowerIrql(NewIrql);
}
Эта функция в основном не позволяет новому IRQL быть выше, чем текущий IRQL, что имеет смысл, поскольку функция должна понижать IRQL. Если все в порядке, функция вызывает HalpLowerIrql(NewIrql);
Это скелет многопроцессорной реализации AMD64 - она фактически не реализует записи в регистр APIC (или MSR для x2APIC), они являются пустыми функциями в многопроцессорной реализации ReactOS AMD64, как это в развитии; но на окнах они не будут, и они фактически запрограммируют LAPIC TPR так, чтобы теперь могло происходить программное прерывание в очереди.
HalpLowerIrql(KIRQL NewIrql, BOOLEAN FromHalEndSystemInterrupt)
{
ULONG Flags;
UCHAR DpcRequested;
if (NewIrql >= DISPATCH_LEVEL)
{
KeSetCurrentIrql (NewIrql);
APICWrite(APIC_TPR, IRQL2TPR (NewIrql) & APIC_TPR_PRI);
return;
}
Flags = __readeflags();
if (KeGetCurrentIrql() > APC_LEVEL)
{
KeSetCurrentIrql (DISPATCH_LEVEL);
APICWrite(APIC_TPR, IRQL2TPR (DISPATCH_LEVEL) & APIC_TPR_PRI);
DpcRequested = __readfsbyte(FIELD_OFFSET(KIPCR, HalReserved[HAL_DPC_REQUEST]));
if (FromHalEndSystemInterrupt || DpcRequested)
{
__writefsbyte(FIELD_OFFSET(KIPCR, HalReserved[HAL_DPC_REQUEST]), 0);
_enable();
KiDispatchInterrupt();
if (!(Flags & EFLAGS_INTERRUPT_MASK))
{
_disable();
}
}
KeSetCurrentIrql (APC_LEVEL);
}
if (NewIrql == APC_LEVEL)
{
return;
}
if (KeGetCurrentThread () != NULL &&
KeGetCurrentThread ()->ApcState.KernelApcPending)
{
_enable();
KiDeliverApc(KernelMode, NULL, NULL);
if (!(Flags & EFLAGS_INTERRUPT_MASK))
{
_disable();
}
}
KeSetCurrentIrql (PASSIVE_LEVEL);
}
Во-первых, он проверяет, находится ли новый IRQL выше уровня диспетчеризации, если это так, он просто устанавливает его на себя и записывает в регистр LAPIC TPR и возвращает.Если нет, он проверяет, является ли текущий IRQL уровнем диспетчеризации (>APC_LEVEL
).Это означает, что по определению новый IRQL будет на меньше , чем уровень диспетчеризации.Мы можем видеть, что в этом случае он становится равным DISPATCH_LEVEL
, а не опускается ниже и записывает его в регистр LAPIC TPR.Затем он проверяет значение HalReserved[HAL_DPC_REQUEST]
, которое, по-видимому, используется ReactOS вместо DpcInterruptRequested
, который мы видели ранее, поэтому просто замените его этим.Затем он устанавливает его на 0 (обратите внимание, что PCR начинается с начала дескриптора сегмента, на который указывает сегмент FS в режиме ядра).Затем он разрешает прерывания и вызывает KiDispatchInterrupt
, и после этого, если регистр eflags
изменил флаг IF во время KiDispatchInterrupt
, он отключает прерывания.Затем он также проверяет, ожидает ли ядро APC (что выходит за рамки этого объяснения), прежде чем окончательно установить IRQL на пассивный уровень
VOID NTAPI KiDispatchInterrupt ( VOID )
{
PKIPCR Pcr = (PKIPCR)KeGetPcr();
PKPRCB Prcb = &Pcr->Prcb;
PKTHREAD NewThread, OldThread;
/* Disable interrupts */
_disable();
/* Check for pending timers, pending DPCs, or pending ready threads */
if ((Prcb->DpcData[0].DpcQueueDepth) ||
(Prcb->TimerRequest) ||
(Prcb->DeferredReadyListHead.Next))
{
/* Retire DPCs while under the DPC stack */
//KiRetireDpcListInDpcStack(Prcb, Prcb->DpcStack);
// FIXME!!! //
KiRetireDpcList(Prcb);
}
/* Re-enable interrupts */
_enable();
/* Check for quantum end */
if (Prcb->QuantumEnd)
{
/* Handle quantum end */
Prcb->QuantumEnd = FALSE;
KiQuantumEnd();
}
else if (Prcb->NextThread)
{
/* Capture current thread data */
OldThread = Prcb->CurrentThread;
NewThread = Prcb->NextThread;
/* Set new thread data */
Prcb->NextThread = NULL;
Prcb->CurrentThread = NewThread;
/* The thread is now running */
NewThread->State = Running;
OldThread->WaitReason = WrDispatchInt;
/* Make the old thread ready */
KxQueueReadyThread(OldThread, Prcb);
/* Swap to the new thread */
KiSwapContext(APC_LEVEL, OldThread);
}
}
Во-первых, он отключает прерывания _disable - это просто оболочкаблок asm, который очищает флаг IF и имеет память и cc в списке clobber (для предотвращения переупорядочения компилятора).Это похоже на синтаксис arm.
{
__asm__ __volatile__
(
"cpsid i @ __cli" : : : "memory", "cc"
);
}
Это гарантирует, что он может использовать очередь DPC как непрерывную процедуру;как и при отключенных прерываниях, оно не может быть прервано прерыванием по времени и перенесено.Это предотвращает сценарий запуска двух планировщиков одновременно, например, если поток с Sleep()
завершает вызов KeRaiseIrqlToSynchLevel
, что аналогично отключению прерываний.Это предотвратит прерывание таймером, прервав его и запланировав переключение другого потока поверх текущей выполняемой процедуры переключения потоков - это гарантирует, что планирование является атомарным.
Он проверяет, есть ли DPC в нормальной очередитекущего ядра или наличия истечения таймера или отложенных готовых потоков, а затем вызывает KiRetireDpcList
, который в основном содержит глубину очереди while! = 0, которая сначала проверяет, является ли это запросом истечения таймера(в который я сейчас не буду вдаваться), если нет, получает спин-блокировку очереди DPC, снимает DPC с очереди и разбирает членов на аргументы (прерывания все еще отключены), уменьшает глубину очереди, сбрасывает спин-блокировку, включает прерывания и вызываетDeferredRoutine
.Когда возвращается DeferredRoutine
, он снова отключает прерывания, и, если в очереди их больше, он снова получает спин-блокировку (блокировка блокировки и прерывания гарантируют, что удаление DPC из очереди является атомарным, так что другое прерывание и, следовательно, утечка очереди DPC не работаютна том же ЦОДе - он уже будет удален из очереди).Поскольку спин-блокировка очереди DPC еще не реализована в ReactOS, мы можем постулировать, что может произойти с окнами: если он не может получить спин-блокировку, то, учитывая, что это спин-блокировка и что мы все еще находимся на DISPATCH_LEVEL
, а прерывания отключены, он будет вращатьсяпока поток на другом ядре не вызовет KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);
, что не так уж и сильно, поскольку каждый поток имеет спин-блокировку в течение примерно 100 моп, я бы сказал, поэтому мы можем позволить отключить прерывания на DISPATCH_LEVEL
.
Обратите внимание, что процедура слива только когда-либо истощает очередь текущего ядра.Когда очередь DPC пуста, она активирует прерывания и проверяет наличие отложенных готовых потоков и делает их все готовыми.Затем он возвращает вниз цепочку вызовов на KiInterruptTemplate
, а затем ISR официально завершается.
Итак, в качестве обзора, в KeInsertQueuedpc
, если DPC для очереди относится к другому ядру и имеет высокий приоритет, или глубина очереди превышает максимум, определенный в PRCB, тогда он устанавливает флаг DpcRequested в PRCBcore и отправляет IPI ядру, которое, скорее всего, каким-то образом запускает KiDispatchInterrupt
(ISR может быть просто процедурой IRQL более низкого уровня, которая на самом деле вызывает KiDispatchinterrupt
), которая истощает очередь DPC на этом ядре;фактическая оболочка, которая вызывает KiDispatchinterrupt
, может или не может отключить флаг DpcRequested в PRCB, как HalpLowerIrql
, но я не знаю, это действительно может быть HalpLowerIrql
, как я и предлагал.После KeInsertQueuedpc
, когда он понижает IRQL, ничего не происходит, потому что флаг DpcRequested находится в другом ядре, а не в текущем ядре.Если DPC для очереди нацелен на текущее ядро, то если оно имеет высокий или средний приоритет или глубина очереди превысила максимальную глубину очереди, и скорость DPC меньше минимальной скорости, определенной в PRCB, тогда он устанавливает флаг DpcRequestedв PRCB и запрашивает собственный IPI, который будет вызывать ту же универсальную оболочку, которая используется планировщиком, так что, вероятно, что-то вроде HalpLowerIrql
.После KeInsertQueuedpc
он понижает IRQL с HalpLowerIrql
и видит DpcRequested
, поэтому сливает очередь текущего ядра перед понижением IRQL.
Вы видите проблему с этим, хотя?WRK показывает запрашиваемое «программное» прерывание (чей ISR, вероятно, вызывает KiDispatchInterrupt
, поскольку это многоцелевая функция, и когда-либо используется только одна функция: KiRequestSoftwareInterrupt(DISPATCH_LEVEL) in all scenarios
), но затем ReactOS показывает, что KiDispatchInterrupt
вызывается, когдаIRQL также падает .Вы ожидаете, что когда KiInterruptDispatch
удалит спин-блокировку ISR, функция, которая сделает это, просто проверит наличие отложенных готовых потоков или запрос истечения таймера, а затем просто отбросит IRQL, потому что программное прерывание для опустошения очереди произойдет, как толькоLAPIC TPR запрограммирован, но ReactOS фактически проверяет элементы в очереди (используя флаг в PRCB) и инициирует слив очереди в процедуре, чтобы понизить IRQL.Не существует исходного кода WRK для освобождения спин-блокировки, но давайте предположим, что он просто не делает то, что происходит в ReactOS, и позволяет «программному» прерыванию обрабатывать его - возможно, он оставляет эту проверку всей очереди DPC вне эквивалента HalpLowerIrql
.Но подождите секунду, что тогда означает Prcb->DpcInterruptRequested
, если он не используется для инициации опустошения очереди, как в ReactOS?Возможно, он просто используется в качестве управляющей переменной, чтобы не ставить в очередь 2 программных прерывания.Также отметим, что ReactOS также запрашивает «программное» прерывание на этом этапе (для контроллера Vectored Interrupt Controller), что крайне странно.Так что, может быть, не тогда.Это явно говорит о том, что его вызывают дважды.Похоже, что она истощает очередь, а затем сразу же после «прерывания» IRQL (который, скорее всего, также вызывает KiRetireDpcList
на некотором этапе) как в ReactOS, так и в WRK поступает «программное» прерывание, что делает то же самое.Интересно, что кто-нибудь делает из этого?Я имею в виду, почему оба Self IPI, а затем истощать очередь в любом случае?Одно из этих действий является избыточным.
Что касается ленивого IRQL.Я не вижу доказательств этого на WRK или ReactOS, но где это будет реализовано, будет KiInterruptDispatch
.Можно было бы получить текущий IRQL, используя KeGetCurrentIrql
, а затем сравнить его с IRQL объекта прерывания и затем , запрограммировав TPR для соответствия текущему IRQL.Он либо прерывает прерывание и ставит в очередь другое для этого вектора, используя собственный IPI, либо просто переключает кадры прерываний.