Work Queues and Dispatcher Headers
Introduction
Hi everyone, Bob here again with a description of Work Queues and Dispatcher Headers. For those of you that look at dumps, you may have noticed that there are always threads waiting at KeRemoveQueue. You may have wondered what this function does. Well, I’m glad you asked… J
What are those threads doing?
Those threads waiting on the Remove Queue are worker threads. Worker threads are used when a system task cannot or does not want to do a particular task. For example, a thread running a DPC cannot pend and wait for a task to be done, so it sends the work to a worker thread.
How does this mechanism work?
The worker thread and the entities that are going to give the worker thread work, each know of a KQUEUE structure. The KQUEUE structure is initialized and, since the queue has an embedded dispatcher object, the worker thread pends on it waiting to be signaled. That is what you see on one of these waiting stacks.
Below is a KQUEUE:
typedef struct _KQUEUE {
DISPATCHER_HEADER Header;
LIST_ENTRY EntryListHead;
ULONG CurrentCount;
ULONG MaximumCount;
LIST_ENTRY ThreadListHead;
} KQUEUE, *PKQUEUE, *RESTRICTED_POINTER PRKQUEUE;
Below is an example of a waiter:
Priority 9 BasePriority 9 PriorityDecrement 0
Child-SP RetAddr Call Site
fffffadc`b053dab0 fffff800`01027752 nt!KiSwapContext+0x85
fffffadc`b053dc30 fffff800`01024ef0 nt!KiSwapThread+0x3c9 ß Waits on the dispatcher object
fffffadc`b053dc90 fffffadc`b9a380b0 nt!KeRemoveQueue+0x656
fffffadc`b053dd10 fffff800`0124b972 srv!WorkerThread+0xb0
fffffadc`b053dd70 fffff800`010202d6 nt!PspSystemThreadStartup+0x3e
fffffadc`b053ddd0 00000000`00000000 nt!KxStartSystemThread+0x16
What is a dispatcher object?
A dispatcher object can be passed into kernel routines such as KeWaitForSingleObject. This object is a synchronization object. This means that a thread can wait on this object until another thread “signals” it. The function KeRemoveQueue is waiting for its dispatcher object to be “signaled”.
Below is a dispatcher object. Basically threads are queued on this object until the object is “signaled”. Once that happens the waiting thread is readied for execution.
nt!_DISPATCHER_HEADER
+0x000 Type : UChar
+0x001 Absolute : UChar
+0x001 NpxIrql : UChar
+0x002 Size : UChar
+0x002 Hand : UChar
+0x003 Inserted : UChar
+0x003 DebugActive : UChar
+0x000 Lock : Int4B
+0x004 SignalState : Int4B ßSet when the object is signaled.
+0x008 WaitListHead : _LIST_ENTRY ßList of waiters on this object.
Below is an actual dispatcher object for a queue:
5: kd> dt nt!_dispatcher_header fffffadcdb3ed368
nt!_DISPATCHER_HEADER
+0x000 Type : 0x4 ''
+0x001 Absolute : 0 ''
+0x001 NpxIrql : 0 ''
+0x002 Size : 0x10 ''
+0x002 Hand : 0x10 ''
+0x003 Inserted : 0 ''
+0x003 DebugActive : 0 ''
+0x000 Lock : 1048580
+0x004 SignalState : 0
+0x008 WaitListHead : _LIST_ENTRY [ 0xfffffadc`db3f4ce8 - 0xfffffadc`da74dce8 ] ß List of threads waiting for this object
Each thread has a Wait List entry for each object it is waiting for:
5: kd> dt nt!_KWAIT_BLOCK 0xfffffadc`db3f4ce8
+0x000 WaitListEntry : _LIST_ENTRY [ 0xfffffadc`da74dce8 - 0xfffffadc`db3ed370 ] ßNext thread waiting for this object
+0x010 Thread : 0xfffffadc`db3f4bf0 _KTHREAD ßThe thread waiting for the object
+0x018 Object : 0xfffffadc`db3ed368 ßThe object the thread is waiting for (queue object)
+0x020 NextWaitBlock : 0xfffffadc`db3f4ce8 _KWAIT_BLOCK ßNext object this thread is waiting for (thread 0xfffffadc`db3f4bf0) if any.
+0x028 WaitKey : 0
+0x02a WaitType : 0x1 ''
+0x02b SpareByte : 0 ''
+0x02c SpareLong : 1533340
What wakes up or signals the thread?
When the thread is waiting, an entity can call KeInsertQueue to insert elements in the work queue. When that event happens the thread is woken up and the system will remove the entry from the work queue and the call from KeRemoveQueue will return with the element. If the thread is not waiting when the call is made, the dispatcher object is put in the queue and the next call to KeRemoveQueue will not pend.
What about synchronization objects?
When one thread wants to synchronize with another, a synchronization object (such as an event) is used. When a thread waits for an event, another thread will signal the event when a job is done such as I/O. The dispatcher objects above are used for all the synchronization objects. As you can see by how the structures are designed, one thread can wait for many objects.
Below this thread is waiting for a synchronization object.
THREAD fffffadff752b040 Cid 0004.2858
fffffadcbe1c3768 NotificationEvent ßObject thread is waiting for .
Not impersonating
DeviceMap fffffa80000840f0
Owning Process fffffadce06e15a0 Image: System
Wait Start TickCount 49664324 Ticks: 247591 (0:01:04:28.609)
Context Switch Count 1
UserTime 00:00:00.000
KernelTime 00:00:00.000
Start Address EmcpBase (0xfffffadcbe22d810)
Stack Init fffffadcb870be00 Current fffffadcb870b940
Base fffffadcb870c000 Limit fffffadcb8706000 Call 0
Priority 8 BasePriority 8 PriorityDecrement 0
Child-SP RetAddr : Args to Child : Call Site
fffffadc`b870b980 fffff800`01027752 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiSwapContext+0x85
fffffadc`b870bb00 fffff800`0102835e : 00000000`00000000 00000000`00000000 fffffadf`f752b0d8 fffffadf`f752b040 : nt!KiSwapThread+0x3c9
fffffadc`b870bb60 fffffadc`be21832b : 00000000`00000000 fffff800`00000000 00000000`00000000 fffffadc`be88b100 : nt!KeWaitForSingleObject+0x5a6
fffffadc`b870bbe0 fffffadc`be1bd0da : 00000000`00000004 00000000`00000000 fffffadc`be239c40 00000000`00000000 : EmcpBase+0xb32b
fffffadc`b870bc20 fffffadc`be22c9a1 : 00000000`00000000 fffffadc`b870bd08 fffffadc`be239c40 fffffadc`e06f6fe0 : EmcpMPAA+0xd0da
fffffadc`b870bc70 fffffadc`be22d90b : fffffadc`da2338c0 00000000`00000001 fffffadc`d9eb3c10 fffffadc`b870bd08 : EmcpBase+0x1f9a1
fffffadc`b870bce0 fffff800`0124b972 : fffffadc`d9f85780 fffffadf`f752b040 00000000`00000080 fffffadf`f752b040 : EmcpBase+0x2090b
fffffadc`b870bd70 fffff800`010202d6 : fffff800`011b1180 fffffadf`f752b040 fffff800`011b5500 00000000`00000000 : nt!PspSystemThreadStartup+0x3e
fffffadc`b870bdd0 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x16
Dispatcher header from address above:
5: kd> dt nt!_dispatcher_header fffffadcbe1c3768
nt!_DISPATCHER_HEADER
+0x000 Type : 0 ''
+0x001 Absolute : 0 ''
+0x001 NpxIrql : 0 ''
+0x002 Size : 0x6 ''
+0x002 Hand : 0x6 ''
+0x003 Inserted : 0 ''
+0x003 DebugActive : 0 ''
+0x000 Lock : 393216
+0x004 SignalState : 0
+0x008 WaitListHead : _LIST_ENTRY [ 0xfffffadf`f752b138 - 0xfffffadf`f752b138 ]
Wait block for this thread:
5: kd> dt 0xfffffadf`f752b138 _KWAIT_BLOCK
nt!_KWAIT_BLOCK
+0x000 WaitListEntry : _LIST_ENTRY [ 0xfffffadc`be1c3770 - 0xfffffadc`be1c3770 ]
+0x010 Thread : 0xfffffadf`f752b040 _KTHREAD
+0x018 Object : 0xfffffadc`be1c3768
+0x020 NextWaitBlock : 0xfffffadf`f752b138 _KWAIT_BLOCK
+0x028 WaitKey : 0
+0x02a WaitType : 0x1 ''
+0x02b SpareByte : 0 ''
+0x02c SpareLong : 1
Conclusion
I hope this gives a better understanding of Work Queues and Dispatcher Headers. More detailed information can be found here: https://msdn2.microsoft.com/en-us/library/ms810047.aspx and here: https://www.microsoft.com/whdc/driver/kernel/locks.mspx.
Comments
- Anonymous
June 13, 2009
PingBack from http://quickdietsite.info/story.php?id=4915