DPC

DPC不同APCDPC的全名是‘延迟过程调用

DPC最初作用是设计为中断服务程序的一部分。因为每次触发中断,都会关中断,然后执行中断服务例程。由于关中断了,所以中断服务例程必须短小精悍,不能消耗过多时间,否则会导致系统丢失大量其他中断。但是有的中断,其中断服务例程要做的事情本来就很多,那怎么办?于是,可以在中断服务例程中先执行最紧迫的那部分工作,然后把剩余的相对来说不那么重要的工作移入到DPC函数中去执行。因此,DPC又叫ISR的后半部。(比如每次时钟中断后,其isr会扫描系统中的所有定时器是否到点,若到点就调用各定时器的函数。但是这个扫描过程比较耗时,因此,时钟中断的isr会将扫描工作纳入dpc中进行)

每当触发一个中断时,中断服务例程可以在当前cpu中插入一个DPC,当执行完isr,退出isr后, cpu就会扫描它的dpc队列,依次执行里面的每个dpc,当执行完dpc后,才又回到当前线程的中断处继续执行。

typedef struct _KDPC {

  UCHAR
Type; //DPC类型(分为普通DPC和线程化DPC

  UCHAR
Importance;//该DPC的重要性,将决定挂在队列头还是尾

  volatile
USHORT Number;//第5位为0就表示当前cpu,否则,最低4位表示目标cpu

  LIST_ENTRY
DpcListEntry;//用来挂入dpc链表

  PKDEFERRED_ROUTINE
DeferredRoutine;//dpc函数

  PVOID
DeferredContext;//dpc函数的参数

  PVOID
SystemArgument1;//挂入时的系统附加参数1

  PVOID
SystemArgument2;//挂入时的系统附加参数2

  volatile
PVOID DpcData;//所在的dpc队列

}
KDPC, *PKDPC,
*RESTRICTED_POINTER PRKDPC;

 

VOID

KeInitializeDpc(IN PKDPC
Dpc,//DPC对象(DPC也是一种内核对象)

                IN PKDEFERRED_ROUTINE
DeferredRoutine, //DPC函数

                IN PVOID DeferredContext)//DPC函数的参数

{

    KiInitializeDpc(Dpc, DeferredRoutine,
DeferredContext, DpcObject);

}

VOID

KiInitializeDpc(IN PKDPC
Dpc,

                IN PKDEFERRED_ROUTINE
DeferredRoutine,

                IN PVOID DeferredContext,

                IN KOBJECTS Type)

{

    Dpc->Type = Type;

    Dpc->Number = 0;//初始的目标cpu为当前cpu

    Dpc->Importance= MediumImportance;

    Dpc->DeferredRoutine = DeferredRoutine;

    Dpc->DeferredContext = DeferredContext;

    Dpc->DpcData = NULL;//表示该DPC尚未挂入任何DPC队列

}

DPC初始化构造时的目标cpu默认都是当前cpu

BOOLEAN  KeInsertQueueDpc(IN PKDPC Dpc,IN PVOID SystemArgument1,IN PVOID SystemArgument2)

{

    KIRQL
OldIrql;

    PKPRCB
Prcb, CurrentPrcb;

    ULONG
Cpu;

    PKDPC_DATA
DpcData;

    BOOLEAN
DpcConfigured = FALSE,
DpcInserted = FALSE;

    KeRaiseIrql(HIGH_LEVEL, &OldIrql);//插入过程的中断级是最高的,这个过程不会被打断。

    CurrentPrcb
= KeGetCurrentPrcb();

 

    //检查目标cpu号的第5位为132 = 00100000,就表示其它cpu,低4位表示cpu

    if
(Dpc->Number
>= 32)

    {

        Cpu
= Dpc->Number
– 32;

        Prcb
= KiProcessorBlock[Cpu];

    }

    Else //否则,表示当前cpu

{

Cpu = Prcb->Number;

        Prcb = CurrentPrcb;//目标cpu就是当前cpu

    }

//if 要插入的是一个线程化dpc并且那个cpu启用了线程化dpc机制

    if
((Dpc->Type
== ThreadedDpcObject) && (Prcb->ThreadDpcEnable))

        DpcData
= &Prcb->DpcData[DPC_THREADED]; //目标cpu的线程化dpc队列

    else

        DpcData
= &Prcb->DpcData[DPC_NORMAL];//目标cpu的普通dpc队列

KiAcquireSpinLock(&DpcData->DpcLock);

//if 尚未挂入任何dpc队列

    if
(!InterlockedCompareExchangePointer(&Dpc->DpcData, DpcData, NULL))

    {

        Dpc->SystemArgument1 = SystemArgument1;

        Dpc->SystemArgument2 = SystemArgument2;

        DpcData->DpcQueueDepth++;

        DpcData->DpcCount++;

        DpcConfigured
= TRUE;

        //不管如何,先把dpc挂到目标cpudpc队列中

        if
(Dpc->Importance
== HighImportance)

            InsertHeadList(&DpcData->DpcListHead,
&Dpc->DpcListEntry);

        else

            InsertTailList(&DpcData->DpcListHead,
&Dpc->DpcListEntry);

 

        if
(&Prcb->DpcData[DPC_THREADED] == DpcData)

        {

 

            if
(!(Prcb->DpcThreadActive)
&& !(Prcb->DpcThreadRequested))

                线程化DPCReactOS目前尚不支持,略

         

        }

        Else
//若挂入的是一个普通dpc(最常见),检查是否需要立即发出一个dpc软中断给cpu

        {

            //if 目标cpu当前没在执行dpc,并且它尚未收到dpc中断请求

            if
(!(Prcb->DpcRoutineActive)
&& !(Prcb->DpcInterruptRequested))

            {

                if (Prcb != CurrentPrcb)//if 目标cpu是其它cpu

                {

                    if (((Dpc->Importance == HighImportance)
||

                        (DpcData->DpcQueueDepth
>= Prcb->MaximumDpcQueueDepth))

 &&

                        (!(AFFINITY_MASK(Cpu)
& KiIdleSummary) || (Prcb->Sleeping)))

                    {

                        Prcb->DpcInterruptRequested
= TRUE;

                        DpcInserted = TRUE;
//表示需要立即给cpu发出dpc软中断

                    }

                }

                Else //if 目标cpu就是自身cpu,最常见

                {

                    //一般插入的都是中等重要性的dpc,因此,一般会立即发出一个dpc中断。

                    if ((Dpc->Importance != LowImportance)
||

                        (DpcData->DpcQueueDepth
>= Prcb->MaximumDpcQueueDepth)
||

                        (Prcb->DpcRequestRate
< Prcb->MinimumDpcRate))

                    {

                        Prcb->DpcInterruptRequested
= TRUE;

                        DpcInserted = TRUE;
//表示需要立即给cpu发出dpc软中断

                    }

                }

            }

        }

    }

    KiReleaseSpinLock(&DpcData->DpcLock);

    if
(DpcInserted)//if 需要立即发出一个dpc软中断

    {

        if
(Prcb != CurrentPrcb)

            KiIpiSend(AFFINITY_MASK(Cpu),
IPI_DPC);

        else

            HalRequestSoftwareInterrupt(DISPATCH_LEVEL);//给当前cpu发出一个dpc软中断

    }

    KeLowerIrql(OldIrql);//降低irql

    return
DpcConfigured;

}

如上,这个函数将dpc挂入目标cpu的指定dpc队列(每个cpu有两个dpc队列,一个普通的,一个线程化的)。然后检查当前是否可以立即向目标cpu发出一个dpc软中断,这样,以在下次降低到DISPATCH_LEVEL以下时扫描执行dpc(其实上面的这个函数一般用于在isr中调用,但用户也可以随时手动调用)

(一般来说,挂入的都是中等重要性的dpc,一般在dpc进队的同时就会顺势发出一个dpc中断.)

 

 

下面的函数可用于模拟硬件,向cpu发出任意irql级别的软中断,请求cpu处理执行那种中断。

VOID FASTCALL

HalRequestSoftwareInterrupt(IN KIRQL Irql)//Irql一般是APC_LEVEL/DPC_LEVEL

{

    ULONG
EFlags;

    PKPCR
Pcr = KeGetPcr();

    KIRQL
PendingIrql;

    EFlags
= __readeflags();//保存老的eflags寄存器

    _disable();//关中断

    Pcr->IRR |= (1 << Irql);//关键。标志向cpu发出了一个对应irql级的软中断

PendingIrql = SWInterruptLookUpTable[Pcr->IRR &
3];//IRR后两位表示是否有阻塞的apc中断

//若有阻塞的apc中断,并且当前irqlPASSIVE_LEVEL,立即执行apc。也即在PASSIVE_LEVEL级时发出任意软中断后,会立即检查执行现有的apc中断。

if (PendingIrql
> Pcr->Irql)

 SWInterruptHandlerTable[PendingIrql]();//调用执行apc中断的isr,处理apc中断

    __writeeflags(EFlags);//恢复原eflags寄存器

}

 

DPC函数的执行时机:

我们看一个函数

VOID FASTCALL

KfLowerIrql(IN KIRQL OldIrql)//降回到原irql

{

    ULONG EFlags;

    ULONG
PendingIrql, PendingIrqlMask;

   
PKPCR Pcr
= KeGetPcr();

    PIC_MASK
Mask;

    EFlags
= __readeflags();//保存老的eflags寄存器

    _disable();//关中断

    Pcr->Irql = OldIrql;//降低到指定irql

   

    //检查是否有在目标irql以上的阻塞中的软中断

    PendingIrqlMask
= Pcr->IRR
& FindHigherIrqlMask[OldIrql];

    if
(PendingIrqlMask)

{  

   
//从高位到低位扫描,找到阻塞中的最高irql级的软中断

        BitScanReverse(&PendingIrql, PendingIrqlMask);

        if
(PendingIrql > DISPATCH_LEVEL)

            SWInterruptHandlerTable[PendingIrql]();//处理那个软中断,似乎这儿有问题,应该是

            SWInterruptHandlerTable[31-PendingIrql]();

    }

    __writeeflags(EFlags);//恢复原eflags寄存器

}

 

unsigned char BitScanReverse(ULONG * const Index, unsigned long Mask)

{

    *Index
= 0;

    while
(Mask && ((Mask
& (1 << 31)) == 0))

    {

        Mask
<<= 1;

        ++(*Index);

    }

    return
Mask ? 1 : 0;

}

 

 

各个软中断的处理函数如下:(怀疑这个表的布局有问题)

PHAL_SW_INTERRUPT_HANDLER SWInterruptHandlerTable[20]
=

{

    KiUnexpectedInterrupt,//PASSIVE_LEVEL最低了,永远不会中断别的irql

    HalpApcInterrupt,//APC中断的isr

    HalpDispatchInterrupt2,//DPC中断的isr

    KiUnexpectedInterrupt,

    HalpHardwareInterrupt0,

    HalpHardwareInterrupt1,

    HalpHardwareInterrupt2,

    HalpHardwareInterrupt3,

    HalpHardwareInterrupt4,

    HalpHardwareInterrupt5,

    HalpHardwareInterrupt6,

    HalpHardwareInterrupt7,

    HalpHardwareInterrupt8,

    HalpHardwareInterrupt9,

    HalpHardwareInterrupt10,

    HalpHardwareInterrupt11,

    HalpHardwareInterrupt12,

    HalpHardwareInterrupt13,

    HalpHardwareInterrupt14,

    HalpHardwareInterrupt15

};

 

下面是处理DPC软中断的isr

VOID  HalpDispatchInterrupt2(VOID)

{

    ULONG
PendingIrqlMask, PendingIrql;

    KIRQL
OldIrql;

    PIC_MASK
Mask;

PKPCR Pcr = KeGetPcr();

//这个函数里面会提高irqlDISPACH_LEVEL去扫描执行dpc队列中的所有dpc

    OldIrql
= _HalpDispatchInterruptHandler();//关键函数

Pcr->Irql
= OldIrql;//恢复成原来的irql

//再去检测是否仍有更高irql的阻塞软中断

    PendingIrqlMask
= Pcr->IRR
& FindHigherIrqlMask[OldIrql];

    if
(PendingIrqlMask)

    {

        BitScanReverse(&PendingIrql, PendingIrqlMask);

        if
(PendingIrql > DISPATCH_LEVEL)

            SWInterruptHandlerTable[PendingIrql]();//应该是[31 – PendingIrql]

    }

}

KIRQL _HalpDispatchInterruptHandler(VOID)

{

    KIRQL
CurrentIrql;

    PKPCR
Pcr = KeGetPcr();

    CurrentIrql
= Pcr->Irql;

    Pcr->Irql = DISPATCH_LEVEL;//将irql临时提高到DISPATCH_LEVEL

    Pcr->IRR &= ~(1 << DISPATCH_LEVEL);//清除对应的软中断位

    _enable();//开中断

    KiDispatchInterrupt();//关键函数。开中断后扫描执行所有dpc

    _disable();

    return
CurrentIrql;//返回原irql

}

 

//下面的函数扫描当前cpudpc队列执行所有dpc

KiDispatchInterrupt

{

    push ebx

    mov ebx, PCR[KPCR_SELF]  //ebc = kpcr*

    cli //关中断

    //检查dpc队列是否为空

    mov eax,
[ebx+KPCR_PRCB_DPC_QUEUE_DEPTH]

    or eax,
[ebx+KPCR_PRCB_TIMER_REQUEST]

    or eax,
[ebx+KPCR_PRCB_DEFERRED_READY_LIST_HEAD]

    jz CheckQuantum

    push ebp  //保存

    push dword ptr [ebx+KPCR_EXCEPTION_LIST]
//保存原seh

    mov dword ptr
[ebx+KPCR_EXCEPTION_LIST], -1  //将当前色seh置空

    /* Save the stack and switch to
the DPC Stack */

    mov edx, esp

    mov esp,
[ebx+KPCR_PRCB_DPC_STACK]  //切换为DPC函数专用的内核栈

    push edx //保存原来的内核栈顶位置

 

    mov ecx, [ebx+KPCR_PRCB]

    call @KiRetireDpcList@4      //关键。扫描执行dpc

    pop esp //恢复成原来的内核栈顶

    pop dword ptr
[ebx+KPCR_EXCEPTION_LIST] //恢复

    pop ebp  //恢复

 

CheckQuantum:

    Sti //开中断

    cmp byte ptr
[ebx+KPCR_PRCB_QUANTUM_END], 0 //现在回头检查当前线程的时间片是否耗尽

    jnz QuantumEnd //若已耗尽,直接跳到QuantumEnd处执行线程切换

    cmp byte ptr
[ebx+KPCR_PRCB_NEXT_THREAD], 0  //再检查当前是否有一个抢占者线程

    je Return

 

    /* Make space on the stack to
save registers */

    sub esp, 3 * 4

    mov [esp+8], esi //保存

    mov [esp+4], edi //保存

    mov [esp+0], ebp //保存

    mov edi,
[ebx+KPCR_CURRENT_THREAD]

 

#ifdef CONFIG_SMP //if多处理器

    call _KeRaiseIrqlToSynchLevel@0
//提升irqlSynchLevel

    mov byte ptr
[edi+KTHREAD_SWAP_BUSY], 1 //标记该线程正在进行切换

    /* Acquire the PRCB Lock */

    lock bts dword ptr
[ebx+KPCR_PRCB_PRCB_LOCK], 0

    jnb GetNext

    lea ecx,
[ebx+KPCR_PRCB_PRCB_LOCK]

    call
@KefAcquireSpinLockAtDpcLevel@4

#endif

 

GetNext:

    mov esi,
[ebx+KPCR_PRCB_NEXT_THREAD]

    and dword ptr
[ebx+KPCR_PRCB_NEXT_THREAD], 0

    mov [ebx+KPCR_CURRENT_THREAD],
esi

    mov byte ptr
[esi+KTHREAD_STATE_], Running

    mov byte ptr
[edi+KTHREAD_WAIT_REASON], WrDispatchInt

    mov ecx, edi

    lea edx, [ebx+KPCR_PRCB_DATA]

    call @KiQueueReadyThread@8

    mov cl, APC_LEVEL

    call @KiSwapContextInternal@0

 

#ifdef CONFIG_SMP

    mov cl, DISPATCH_LEVEL

    call @KfLowerIrql@4

#endif

    mov ebp, [esp+0] //恢复

    mov edi, [esp+4] //恢复

    mov esi, [esp+8] //恢复

    add esp, 3*4

Return:

    pop ebx

    ret

QuantumEnd:

    mov byte ptr
[ebx+KPCR_PRCB_QUANTUM_END], 0

    call _KiQuantumEnd@0  //调用这个函数切换线程

    pop ebx

    ret

}

 

上面的函数在执行dpc前会先将内核栈切换为dpc函数专用栈。因为dpc函数运行在任意线程的上下文中,而dpc函数可能太大,局部变量太多而占用了过多的内核栈,所以需要为dpc函数的执行专门配备一个栈。

这个函数还有一个注意地方,就是在扫描dpc队列执行完所有dpc函数后,会检查当前线程的时间片是否耗尽,若耗尽就进行线程切换,若尚未耗尽,就检查当前是否有一个抢占者线程,若有,也进行线程切换。

【总之:系统在每次扫描执行完dpc队列后,都会尝试进行线程切换】

 

下面的函数才是最终扫描执行dpc

VOID FASTCALL

KiRetireDpcList(IN PKPRCB
Prcb)

{

    PKDPC_DATA
DpcData;

    PLIST_ENTRY
ListHead, DpcEntry;

    PKDPC
Dpc;

    PKDEFERRED_ROUTINE
DeferredRoutine;

    PVOID
DeferredContext, SystemArgument1,
SystemArgument2;

    ULONG_PTR
TimerHand;

    DpcData
= &Prcb->DpcData[DPC_NORMAL];//当前cpu的普通dpc队列

    ListHead
= &DpcData->DpcListHead;

    do

    {

        Prcb->DpcRoutineActive = TRUE;//标记当前cpu正在执行dpc

        if
(Prcb->TimerRequest)
//if收到有定时器到期dpc中断(定时器是一种特殊的dpc

        {

            TimerHand
= Prcb->TimerHand;

            Prcb->TimerRequest = 0;

            _enable();

            KiTimerExpiration(NULL, NULL, (PVOID)TimerHand, NULL);//处理定时器队列

            _disable();

        }

        while
(DpcData->DpcQueueDepth
!= 0)//遍历dpc队列

        {

            KeAcquireSpinLockAtDpcLevel(&DpcData->DpcLock);

            DpcEntry
= ListHead->Flink;

            if
(DpcEntry != ListHead)

            {

                RemoveEntryList(DpcEntry);//取下来

                Dpc
= CONTAINING_RECORD(DpcEntry,
KDPC, DpcListEntry);

                Dpc->DpcData = NULL;

                DeferredRoutine = Dpc->DeferredRoutine;

                DeferredContext = Dpc->DeferredContext;

                SystemArgument1 = Dpc->SystemArgument1;

                SystemArgument2 = Dpc->SystemArgument2;

                DpcData->DpcQueueDepth–;

                Prcb->DebugDpcTime
= 0;

                KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);

                _enable();//开中断

                //关键。执行DPC例程

                DeferredRoutine(Dpc,DeferredContext,SystemArgument1,SystemArgument2);

                ASSERT(KeGetCurrentIrql()
== DISPATCH_LEVEL);

                _disable();//关中断

            }

            else

            {

                ASSERT(DpcData->DpcQueueDepth
== 0);//肯定执行完了所有dpc

                KeReleaseSpinLockFromDpcLevel(&DpcData->DpcLock);

            }

        }

        Prcb->DpcRoutineActive = FALSE;//当前不再用dpc正在执行

        Prcb->DpcInterruptRequested = FALSE;

    } while
(DpcData->DpcQueueDepth
!= 0);

}

注意:DPC函数运行在DISPATCH_LEVEL,并且开中断,因此dpc函数本身又可能被其他中断给中断。

因为dpc函数本身就是一种软中断,因此它支持中断嵌套。

综上:

DPC作为一种软中断,也是在irql的降低过程中得到执行的,并且是在从DISPATCH_LEVEL以上(不包括)降低到DISPATCH_LEVEL以下(不包括),也即在穿越DISPATCH_LEVEL的过程中,系统会先暂停在DISPATCH_LEVEL级执行当前cpudpc队列中所有阻塞的dpc,执行完后,再降回到真正的irql

【总之:在降低过程中检查是否有dpc中断,若有执行之】一句口诀:【降低、检断、DPC

不像APC的执行时机有很多,DPC的执行时机就一处。

那么在什么时候,系统会降低irql呢?除了用户显式调用这个内核函数外,isr一般工作在比DISPATCH_LEVEL高的irql,当isr退出时,必然会降低irql到原来的irql。因此,常常在isr中插入一个dpcdpc队列,发出一个dpc中断给cpu,然后退出isr时,降低irql,顺理成章的执行dpc

 

 

 

KeInitializeDpc初始化的dpc,默认的目标cpu都是当前cpu,如果需要将dpc发给其他cpu,让其在其他cpu上运行的话,可以采用下面的函数

VOID

KeSetTargetProcessorDpc(IN PKDPC Dpc,IN CCHAR Number)

{

    Dpc->Number = Number + 32;

}

这是一个非常有用的函数,因为他可以使你的代码运行在你想要的cpu上。比如,你写了一个函数,你只想那个函数运行在3cpu上,那么,你可以构造一个在3cpu上运行的dpc,然后在dpc里调用你自己的函数。这种技术常用于保障内联hook的多cpu线程安全 和 IDT
hook

当然也可使用KeSetSystemAffinityThread这个内核函数,修改当前线程的cpu亲缘性为‘只能运行在目标cpu’上,这样,也会立即导致当前线程立刻挪到其它cpu上去运行,KeSetSystemAffinityThread的代码,有兴趣的读者自己看。

 

 

 

 

 

 

本篇既然谈到了DPC,那就要讲下与之紧密相关的另一个话题:‘系统工作者线程’。

DPC函数是运行在DISPATCH_LEVEL的,而内核中的绝大多数函数的运行时irql都不能处在这个中断级别,如ZwCreateFieddk文档规定了,这个内核函数必须运行在PASSIVE_LEVEL,如果我们需要在某个DPC函数中调用ZwCreateFie,怎么办呢?一个办法便是将这个工作打包成一条工作项委派给‘系统工作者线程’去执行。

内核中有一个守护线程(其实分成9个线程),运行在PASSIVE_LEVEL,专门用来提供服务,执行别的线程委派给它的工作,这个守护线程就是‘系统工作者线程’。

 

按照工作项的紧迫程度,分成三种。系统中相应的有三种工作项队列

typedef enum _WORK_QUEUE_TYPE {

  CriticalWorkQueue,//较紧迫

  DelayedWorkQueue,//最常见

  HyperCriticalWorkQueue,//最紧迫

}
WORK_QUEUE_TYPE;

CriticalWorkQueue工作项队列上配有5个服务线程,DelayedWorkQueue队列上配有3个服务线程,HyperCriticalWorkQueue上配有1个服务线程。

 

下面的宏用来构造一条工作项

#define
ExInitializeWorkItem(Item,Routine,Context) \

{
\

   Item->WorkRoutine=Routine;\

   Item->Parameter=Context;\

   Item->List.Flink=NULL;\

}

 

 

构造好一条工作项后,就可以把这条工作项挂入指定紧迫程度的系统工作项队列中。

VOID

ExQueueWorkItem(IN PWORK_QUEUE_ITEM
WorkItem,

                IN WORK_QUEUE_TYPE QueueType)//工作项紧迫程度类型

{

    PEX_WORK_QUEUE
WorkQueue = &ExWorkerQueue[QueueType];//相应的工作项队列

    if
((ULONG_PTR)WorkItem->WorkerRoutine < MmUserProbeAddress)//必须位于内核空间

    {

        KeBugCheckEx(WORKER_INVALID,1, (ULONG_PTR)WorkItem,

                     (ULONG_PTR)WorkItem->WorkerRoutine,0);

}

 

KeInsertQueue(&WorkQueue->WorkerQueue, &WorkItem->List);//关键。

 

    if
((WorkQueue->Info.MakeThreadsAsNecessary) &&

        (!IsListEmpty(&WorkQueue->WorkerQueue.EntryListHead)) &&

        (WorkQueue->WorkerQueue.CurrentCount
< WorkQueue->WorkerQueue.MaximumCount) &&

        (WorkQueue->DynamicThreadCount < 16))

    {

        KeSetEvent(&ExpThreadSetManagerEvent, 0, FALSE);

    }

}

 

 

当把工作项插入到系统对应的工作项队列后,系统中的某个服务线程便会在某一时刻处理该工作项。

9个服务线程的函数都是同一个函数,只是参数不同。我们看:

VOID ExpWorkerThreadEntryPoint(IN PVOID Context)//context参数主要表示工作项队列类型

{

    PLARGE_INTEGER
TimeoutPointer = NULL;

    PETHREAD
Thread = PsGetCurrentThread();

    if
((ULONG_PTR)Context
& EX_DYNAMIC_WORK_THREAD)

    {

        Timeout.QuadPart = Int32x32To64(10,
-10000000 * 60);//1分钟

        TimeoutPointer
= &Timeout;

    }

    WorkQueueType
= (WORK_QUEUE_TYPE)((ULONG_PTR)Context &~ EX_DYNAMIC_WORK_THREAD);

    WorkQueue
= &ExWorkerQueue[WorkQueueType];

    WaitMode
= (UCHAR)WorkQueue->Info.WaitMode;

    ASSERT(Thread->ExWorkerCanWaitUser
== FALSE);

    if
(WaitMode == UserMode)
Thread->ExWorkerCanWaitUser
= TRUE;

    if
(!ExpWorkersCanSwap) KeSetKernelStackSwapEnable(FALSE);

    do

    {

        if
(WorkQueue->Info.QueueDisabled)

        {

            KeSetKernelStackSwapEnable(TRUE);

            PsTerminateSystemThread(STATUS_SYSTEM_SHUTDOWN);//立即终止

        }

        OldValue
= WorkQueue->Info;

        NewValue
= OldValue;

        NewValue.WorkerCount++;//递增该队列上的工作线程计数

    }

    while
(InterlockedCompareExchange((PLONG)&WorkQueue->Info,*(PLONG)&NewValue,

                                     
*(PLONG)&OldValue)
!= *(PLONG)&OldValue);

    Thread->ActiveExWorker = TRUE;//标记正式成为一个工作者线程了

ProcessLoop:

    for
(;;)

{

   
//等待本服务线程的工作项队列中出现工作项,然后取下来

        QueueEntry
= KeRemoveQueue(&WorkQueue->WorkerQueue,WaitMode,TimeoutPointer);

        if
((NTSTATUS)(ULONG_PTR)QueueEntry == STATUS_TIMEOUT)

 break;//等待超时就退出for循环

        InterlockedIncrement((PLONG)&WorkQueue->WorkItemsProcessed);//递增已处理计数

        WorkItem
= CONTAINING_RECORD(QueueEntry,
WORK_QUEUE_ITEM, List);

 

        WorkItem->WorkerRoutine(WorkItem->Parameter);//关键。调用执行工作项

 

        if
(Thread->Tcb.SpecialApcDisable) 
Thread->Tcb.SpecialApcDisable = FALSE;

 

        //我们的工作项函数运行在PASSIVE_LEVEL,内部不要修改irql,否则蓝屏

        if
(KeGetCurrentIrql() != PASSIVE_LEVEL)

        {

            KeBugCheckEx(WORKER_THREAD_RETURNED_AT_BAD_IRQL,

                         (ULONG_PTR)WorkItem->WorkerRoutine,KeGetCurrentIrql(),

                         (ULONG_PTR)WorkItem->Parameter, (ULONG_PTR)WorkItem);

        }

        if
(Thread->ActiveImpersonationInfo)//工作项函数内部不能冒用令牌

        {

            KeBugCheckEx(IMPERSONATING_WORKER_THREAD, (ULONG_PTR)WorkItem->WorkerRoutine,

                         (ULONG_PTR)WorkItem->Parameter, (ULONG_PTR)WorkItem,0);

        }

    }

    if
(!IsListEmpty(&Thread->IrpList)) goto ProcessLoop; 
//继续服务

    if
(WorkQueue->Info.QueueDisabled) goto ProcessLoop;   //继续服务

    //下面退出服务线程

    do

    {

        OldValue
= WorkQueue->Info;

        NewValue
= OldValue;

        NewValue.WorkerCount–;//递减该队列上的服务线程计数

    }

    while
(InterlockedCompareExchange((PLONG)&WorkQueue->Info,*(PLONG)&NewValue,

                                     
*(PLONG)&OldValue)
!= *(PLONG)&OldValue);

    InterlockedDecrement(&WorkQueue->DynamicThreadCount);

    Thread->ActiveExWorker = FALSE;

    KeSetKernelStackSwapEnable(TRUE);

    return;

}

上面这段代码想必不用过多解释了