November 01, 2002
Inside NT's Asynchronous Procedure Call
Listing 6 The pseudocode for the NT APC dispatcher subroutine KiDeliverApc
//=========================================================================
//
// KiDeliverApc
//
// Copyright (C) 2002 Albert Almeida
//
// Pseudo-code for the NT/2000's APC dispatcher subroutine KiDeliverApc
// in Ntoskrnl.exe.
//
// The kernel always executes this subroutine at APC Level.
// Reserved is always passed as NULL.
//
//=========================================================================
VOID KiDeliverApc(KPROCESSOR_MODE PreviousMode,
PVOID Reserved,
PKTRAP_FRAME TrapFrame)
{
//
// Get a pointer to the currently executing thread from the
// Processor Region Control Block (PRCB)
//
PKTHREAD CurrentThread = PCR->CurrentThread;
//
// Raises the processor's IRQL to Clock2 Level
//
OldIrql = _KeRaiseIrqlToSynchLevel();
CurrentThread->ApcState.KernelApcPending = FALSE;
//
// If kernel-mode APC list is empty then check user-mode APC list
//
if (!IsListEmpty(&CurrentThread->ApcState.ApcListhead[KernelMode]))
{
do
{
//
// Get the APC object at the list head
//
entry = CurrentThread->ApcState.ApcListHead [KernelMode].Flink;
Apc = CONTAINING_RECORD(entry, KAPC, ApcListEntry);
//
// Save the APC attributes in local variables so that
// it's safe to free
// the APC object in KernelRoutine
//
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
//
// If NormalRoutine is NULL this is a special kernel-mode APC
//
if (NormalRoutine == NULL)
{
//
// Dequeue the APC object from the list head
//
RemoveHeadlList(&CurrentThread->ApcState.ApcListHead[KernelMode]);
Apc->Inserted = FALSE;
//
// Lower the IRQL to APC Level
//
_KfLowerIrql(OldIrql);
//
// Note that nothing prevents special kernel
// APCs from executing
//
Kernelroutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
OldIrql = _KeRaiseIrqlToSynchLevel();
}
else
{
//
// We are here is NormalRoutine is non-NULL
// which means this is
// a regular kernel-mode APC. Then check is
// there are any
// regular kernel-mode APCs in progress or
// the thread is inside a
// critical region in which case
// KernelApcDisable is TRUE.
// If any of these conditions hold, return to
// the caller
//
if (CurrentThread->ApcState.KernelApcInProgress ||
CurrentThread->KernelApcDisable) goto restore_irql_and_exit;
//
// Dequeue the APC object from the list head
//
RemoveHeadList(&CurrentThread->ApcState.ApcListHead[KernelMode]);
//
// Clear the kernel-mode Alerted flag
//
CurrentThread->Alerted[0] = FALSE;
_KfLowerIrql(OldIrql);
//
// Call this regular-kernel mode APC
// KernelRoutine
//
Kernelroutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
//
// If the KernelRoutine of this regualr
// kernel-mode APC did not
// clear the NormalRoutine function pointer,
// call this function
// at Passive Level
//
if (NormalRoutine != NULL)
{
//
// Because the NormalRoutine of a
// regular kernel-mode APC
// runs at Passive Level, it can be
// preempted by
// special kernel-mode APCs
//
CurrentThread->ApcState.KernelApcInProgress = TRUE;
_KfLowerIrql(PASSIVE_LEVEL);
NormalRoutine(NormalContext,
SystemArgument1,
SystemArgument2);
OldIrql = _KfRaiseIrql(APC_LEVEL);
}
OldIrql = _KeRaiseIrqlToSynchLevel();
CurrentThread->ApcState.KernelApcInProgress = FALSE;
}
}
while (!IsListEmpty(&CurrentThread->ApcState.ApcListhead [KernelMode]))
}
//
// Check if user-mode APC list is empty
//
if (!IsListEmpty(&CurrentThread->ApcState.ApcListhead[UserMode]))
{
//
// If PreviousMode is not user mode or there are
// no pending user APCs, just return to the caller
//
if (PreviousMode != UserMode ||
CurrentThread->ApcState.UserApcPending == FALSE) goto restore_irql_and_exit;
CurrentThread->ApcState.UserApcPending = FALSE;
//
// Get the APC object at the list head
//
entry = CurrentThread->ApcState.ApcListHead[UserMode].Flink;
Apc = CONTAINING_RECORD(entry, KAPC, ApcListEntry);
//
// Save the APC attributes in local variables so that it×s
// safe
// to free the APC object in KernelRoutine
//
NormalRoutine = Apc->NormalRoutine;
KernelRoutine = Apc->KernelRoutine;
NormalContext = Apc->NormalContext;
SystemArgument1 = Apc->SystemArgument1;
SystemArgument2 = Apc->SystemArgument2;
RemoveHeadList(&CurrentThread->ApcState.ApcListHead[UserMode]);
//
// Clear the kernel-mode Alerted flag
//
CurrentThread->Alerted[0] = FALSE;
//
// Calls this user APC's KernelRoutine
//
Kernelroutine(Apc,
&NormalRoutine,
&NormalContext,
&SystemArgument1,
&SystemArgument2);
if (NormalRoutine == NULL)
{
//
// This function checks whether there are more
// pending user APCs
// and in such a case, set the UserModeApcPendig
// flag to TRUE.
//
_KeTestAlertThread(UserMode);
return;
}
else
{
//
// This function sets up the thread's the trap frame
// to start
// executing in user mode at KiUserApcDispatcher in
// Ntdll.dll
//
_KiInitializeUserApc(Reserved,
TrapFrame,
NormalRoutine,
NormalContext,
SystemArgument1,
SystemArgument2);
return;
}
}
restore_irql_and_exit:
_KfLowerIrql(OldIrql);
}
|
|
||||||||||||||||||||||||||||
|
|