第一部分:
参考例子:应用程序调用kernel32!GetQueuedCompletionStatus后会调用nt!KeRemoveQueue函数进入进入等待状态
0: kd> g
Breakpoint 8 hit
nt!KiDeliverApc:
80a3c776 55 push ebp
0: kd> kc
#
00 nt!KiDeliverApc
01 nt!KiSwapThread
02 nt!KeRemoveQueue
03 nt!NtRemoveIoCompletion
04 nt!_KiSystemService
05 SharedUserData!SystemCallStub
06 ntdll!ZwRemoveIoCompletion
07 kernel32!GetQueuedCompletionStatus
08 RPCRT4!COMMON_ProcessCalls
09 RPCRT4!LOADABLE_TRANSPORT::ProcessIOEvents
0a RPCRT4!ProcessIOEventsWrapper
0b RPCRT4!BaseCachedThreadRoutine
0c RPCRT4!ThreadStartRoutine
0d kernel32!BaseThreadStart
PLIST_ENTRY
KeRemoveQueue (
IN PRKQUEUE Queue,
IN KPROCESSOR_MODE WaitMode,
IN PLARGE_INTEGER Timeout OPTIONAL
)
/*++
Routine Description:
This function removes the next entry from the Queue object entry
list. If no list entry is available, then the calling thread is
put in a wait state.
N.B. The wait discipline for Queue object LIFO.
Arguments:
Queue - Supplies a pointer to a dispatcher object of type Queue.
WaitMode - Supplies the processor mode in which the wait is to occur.
Timeout - Supplies a pointer to an optional absolute of relative time over
which the wait is to occur.
Return Value:
The address of the entry removed from the Queue object entry list or
STATUS_TIMEOUT.
N.B. These values can easily be distinguished by the fact that all
addresses in kernel mode have the high order bit set.
--*/
{
PKPRCB CurrentPrcb;
LARGE_INTEGER DueTime;
PLIST_ENTRY Entry;
LARGE_INTEGER NewTime;
PRKQUEUE OldQueue;
PLARGE_INTEGER OriginalTime;
LOGICAL StackSwappable;
PRKTHREAD Thread;
PRKTIMER Timer;
PRKWAIT_BLOCK WaitBlock;
LONG_PTR WaitStatus;
PRKWAIT_BLOCK WaitTimer;
ASSERT_QUEUE(Queue);
//
// Set constant variables.
//
OriginalTime = Timeout;
Thread = KeGetCurrentThread();
Timer = &Thread->Timer;
WaitBlock = &Thread->WaitBlock[0];
WaitTimer = &Thread->WaitBlock[TIMER_WAIT_BLOCK];
//
// If the dispatcher database lock is already held, then initialize the
// local variables. Otherwise, raise IRQL to SYNCH_LEVEL, initialize the
// thread local variables, and lock the dispatcher database.
//
if (Thread->WaitNext) {
Thread->WaitNext = FALSE;
InitializeRemoveQueue();
} else {
Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
InitializeRemoveQueue();
KiLockDispatcherDatabaseAtSynchLevel();
}
//
// Check if the thread is currently processing a queue entry and whether
// the new queue is the same as the old queue.
//
OldQueue = Thread->Queue;
Thread->Queue = Queue;
if (Queue != OldQueue) {
//
// If the thread was previously associated with a queue, then remove
// the thread from the old queue object thread list and attempt to
// activate another thread.
//
Entry = &Thread->QueueListEntry;
if (OldQueue != NULL) {
RemoveEntryList(Entry);
KiActivateWaiterQueue(OldQueue);
}
//
// Insert thread in the thread list of the new queue that the thread
// will be associate with.
//
InsertTailList(&Queue->ThreadListHead, Entry);
} else {
//
// The previous and current queue are the same queue - decrement the
// current number of threads.
//
Queue->CurrentCount -= 1;
}
//
// Start of wait loop.
//
// Note this loop is repeated if a kernel APC is delivered in the
// middle of the wait or a kernel APC is pending on the first attempt
// through the loop.
//
// If the Queue object entry list is not empty, then remove the next
// entry from the Queue object entry list. Otherwise, wait for an entry
// to be inserted in the queue.
//
do {
//
// Check if there is a queue entry available and the current
// number of active threads is less than target maximum number
// of threads.
//
Entry = Queue->EntryListHead.Flink;
if ((Entry != &Queue->EntryListHead) &&
(Queue->CurrentCount < Queue->MaximumCount)) {
//
// Decrement the number of entires in the Queue object entry list,
// increment the number of active threads, remove the next entry
// from the list, and set the forward link to NULL.
//
Queue->Header.SignalState -= 1;
Queue->CurrentCount += 1;
if ((Entry->Flink == NULL) || (Entry->Blink == NULL)) {
KeBugCheckEx(INVALID_WORK_QUEUE_ITEM,
(ULONG_PTR)Entry,
(ULONG_PTR)Queue,
(ULONG_PTR)&ExWorkerQueue[0],
(ULONG_PTR)((PWORK_QUEUE_ITEM)Entry)->WorkerRoutine);
}
RemoveEntryList(Entry);
Entry->Flink = NULL;
break;
} else {
//
// Test to determine if a kernel APC is pending.
//
// If a kernel APC is pending, the special APC disable count is
// zero, and the previous IRQL was less than APC_LEVEL, then a
// kernel APC was queued by another processor just after IRQL was
// raised to DISPATCH_LEVEL, but before the dispatcher database
// was locked.
//
// N.B. that this can only happen in a multiprocessor system.
//
if (Thread->ApcState.KernelApcPending &&
(Thread->SpecialApcDisable == 0) &&
(Thread->WaitIrql < APC_LEVEL)) {
//
// Increment the current thread count, unlock the dispatcher
// database, and exit the dispatcher. An APC interrupt will
// immediately occur which will result in the delivery of the
// kernel APC, if possible.
//
Queue->CurrentCount += 1;
KiRequestSoftwareInterrupt(APC_LEVEL);
KiUnlockDispatcherDatabaseFromSynchLevel();
KiExitDispatcher(Thread->WaitIrql);
} else {
//
// Test if a user APC is pending.
//
if ((WaitMode != KernelMode) && (Thread->ApcState.UserApcPending)) {
Entry = (PLIST_ENTRY)ULongToPtr(STATUS_USER_APC);
Queue->CurrentCount += 1;
break;
}
//
// Check to determine if a timeout value is specified.
//
if (ARGUMENT_PRESENT(Timeout)) {
//
// If the timeout value is zero, then return immediately
// without waiting.
//
if (!(Timeout->LowPart | Timeout->HighPart)) {
Entry = (PLIST_ENTRY)ULongToPtr(STATUS_TIMEOUT);
Queue->CurrentCount += 1;
break;
}
//
// Insert the timer in the timer tree.
//
// N.B. The constant fields of the timer wait block are
// initialized when the thread is initialized. The
// constant fields include the wait object, wait key,
// wait type, and the wait list entry link pointers.
//
if (KiInsertTreeTimer(Timer, *Timeout) == FALSE) {
Entry = (PLIST_ENTRY)ULongToPtr(STATUS_TIMEOUT);
Queue->CurrentCount += 1;
break;
}
DueTime.QuadPart = Timer->DueTime.QuadPart;
}
//
// Insert wait block in object wait list.
//
InsertTailList(&Queue->Header.WaitListHead, &WaitBlock->WaitListEntry);
//
// Set the thread wait parameters, set the thread dispatcher
// state to Waiting, and insert the thread in the wait list.
//
CurrentPrcb = KeGetCurrentPrcb();
Thread->State = Waiting;
if (StackSwappable != FALSE) {
InsertTailList(&CurrentPrcb->WaitListHead, &Thread->WaitListEntry);
}
//
// Set swap busy for the current thread, unlock the dispatcher
// database, and switch to a new thread.
//
// Control is returned at the original IRQL.
//
ASSERT(Thread->WaitIrql <= DISPATCH_LEVEL);
KiSetContextSwapBusy(Thread);
KiUnlockDispatcherDatabaseFromSynchLevel();
WaitStatus = KiSwapThread(Thread, CurrentPrcb);
//
// If the thread was not awakened to deliver a kernel mode APC,
// then return wait status.
//
Thread->WaitReason = 0;
if (WaitStatus != STATUS_KERNEL_APC) {
return (PLIST_ENTRY)WaitStatus;
}
if (ARGUMENT_PRESENT(Timeout)) {
//
// Reduce the amount of time remaining before timeout occurs.
//
Timeout = KiComputeWaitInterval(OriginalTime,
&DueTime,
&NewTime);
}
}
//
// Raise IRQL to SYNCH level, initialize the local variables,
// lock the dispatcher database, and decrement the count of
// active threads.
//
Thread->WaitIrql = KeRaiseIrqlToSynchLevel();
InitializeRemoveQueue();
KiLockDispatcherDatabaseAtSynchLevel();
Queue->CurrentCount -= 1;
}
} while (TRUE);
//
// Unlock the dispatcher database, exit the dispatcher, and return the
// list entry address or a status of timeout.
//
KiUnlockDispatcherDatabaseFromSynchLevel();
KiExitDispatcher(Thread->WaitIrql);
return Entry;
}
第二部分:
OldQueue = Thread->Queue;
Thread->Queue = Queue;
if (Queue != OldQueue) {
//
// If the thread was previously associated with a queue, then remove
// the thread from the old queue object thread list and attempt to
// activate another thread.
//
Entry = &Thread->QueueListEntry;
if (OldQueue != NULL) {
RemoveEntryList(Entry);
KiActivateWaiterQueue(OldQueue);
}
//
// Insert thread in the thread list of the new queue that the thread
// will be associate with.
//
InsertTailList(&Queue->ThreadListHead, Entry);
1: kd> dt kthread 0x89848268
ntdll!KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY [ 0x89848278 - 0x89848278 ]
+0x018 InitialStack : 0xbaac8000 Void
+0x01c StackLimit : 0xbaac5000 Void
+0x020 KernelStack : 0xbaac7c38 Void
+0x024 ThreadLock : 0
+0x028 ContextSwitches : 2
+0x02c State : 0x5 ''
+0x100 QueueListEntry : _LIST_ENTRY [ 0x895f3b28 - 0x895f3b28 ]
1: kd> dx -id 0,0,898d9250 -r1 (*((ntdll!_LIST_ENTRY *)0x89848368))
(*((ntdll!_LIST_ENTRY *)0x89848368)) [Type: _LIST_ENTRY]
[+0x000] Flink : 0x895f3b28 [Type: _LIST_ENTRY *]
[+0x004] Blink : 0x895f3b28 [Type: _LIST_ENTRY *]
第三部分:
PLIST_ENTRY
KeRemoveQueue (
IN PRKQUEUE Queue,
IN KPROCESSOR_MODE WaitMode,
IN PLARGE_INTEGER Timeout OPTIONAL
)
{
if (Thread->ApcState.KernelApcPending &&
(Thread->SpecialApcDisable == 0) &&
(Thread->WaitIrql < APC_LEVEL)) {
。。。。。。
} else {
//
// Insert wait block in object wait list.
//
InsertTailList(&Queue->Header.WaitListHead, &WaitBlock->WaitListEntry);
1: kd> dx -id 0,0,898d9250 -r1 (*((GDI32!_DISPATCHER_HEADER *)0x895f3b08))
(*((GDI32!_DISPATCHER_HEADER *)0x895f3b08)) [Type: _DISPATCHER_HEADER]
[+0x000] Type : 0x4 [Type: unsigned char]
[+0x001] Absolute : 0x0 [Type: unsigned char]
[+0x002] Size : 0xa [Type: unsigned char]
[+0x003] Inserted : 0x0 [Type: unsigned char]
[+0x003] DebugActive : 0x0 [Type: unsigned char]
[+0x000] Lock : 655364 [Type: long]
[+0x004] SignalState : 0 [Type: long]
[+0x008] WaitListHead [Type: _LIST_ENTRY]
1: kd> dx -id 0,0,898d9250 -r1 (*((GDI32!_LIST_ENTRY *)0x895f3b10))
(*((GDI32!_LIST_ENTRY *)0x895f3b10)) [Type: _LIST_ENTRY]
[+0x000] Flink : 0x89848308 [Type: _LIST_ENTRY *]
[+0x004] Blink : 0x89848308 [Type: _LIST_ENTRY *]
1: kd> dt kthread 0x89848308-a0
ntdll!KTHREAD
+0x000 Header : _DISPATCHER_HEADER
+0x010 MutantListHead : _LIST_ENTRY [ 0x89848278 - 0x89848278 ]
+0x018 InitialStack : 0xbaac8000 Void
+0x01c StackLimit : 0xbaac5000 Void
+0x020 KernelStack : 0xbaac7c38 Void
+0x024 ThreadLock : 0
+0x028 ContextSwitches : 2
+0x02c State : 0x5 ''
+0x0a0 WaitBlock : [4] _KWAIT_BLOCK