关于DPC的 文章 留着备用
原文地址 http://www.doudouxitong.com/guzhang/xitongjiqiao/2014/0404/901.html

  DPC不同APC,DPC的全名是‘延迟过程调用’。

  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位为1(32 = 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挂到目标cpu的dpc队列中

  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))

  线程化DPC,ReactOS目前尚不支持,略

  }

  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中断,并且当前irql是PASSIVE_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();

  //这个函数里面会提高irql到DISPACH_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

  }

  //下面的函数扫描当前cpu的dpc队列执行所有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 //提升irql到SynchLevel

  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级执行当前cpu的dpc队列中所有阻塞的dpc,执行完后,再降回到真正的irql。

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

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

  那么在什么时候,系统会降低irql呢?除了用户显式调用这个内核函数外,isr一般工作在比DISPATCH_LEVEL高的irql,当isr退出时,必然会降低irql到原来的irql。因此,常常在isr中插入一个dpc到dpc队列,发出一个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上。比如,你写了一个函数,你只想那个函数运行在3号cpu上,那么,你可以构造一个在3号cpu上运行的dpc,然后在dpc里调用你自己的函数。这种技术常用于保障内联hook的多cpu线程安全 和 IDT hook。

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

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

  DPC函数是运行在DISPATCH_LEVEL的,而内核中的绝大多数函数的运行时irql都不能处在这个中断级别,如ZwCreateFie,ddk文档规定了,这个内核函数必须运行在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;

  }

windows内核情景分析 --- DPC 目的信令点编码相关推荐

  1. [9]Windows内核情景分析 --- DPC

    DPC不同APC,DPC的全名是'延迟过程调用'. DPC最初作用是设计为中断服务程序的一部分.因为每次触发中断,都会关中断,然后执行中断服务例程.由于关中断了,所以中断服务例程必须短小精悍,不能消耗 ...

  2. windows 内核情景分析

    原文很长:先转部分过来,有时间看一下: 一 windows 内核情景分析---说明 说明 本文结合<Windows内核情景分析>(毛德操著).<软件调试>(张银奎著).< ...

  3. [14]Windows内核情景分析 --- 文件系统

    文件系统 一台机器上可以安装很多物理介质来存放资料(如磁盘.光盘.软盘.U盘等).各种物理介质千差万别,都配备有各自的驱动程序,为了统一地访问这些物理介质,windows设计了文件系统机制.应用程序要 ...

  4. windows内核情景分析 --- 文件系统

    文件系统 一台机器上可以安装很多物理介质来存放资料(如磁盘.光盘.软盘.U盘等).各种物理介质千差万别,都配备有各自的驱动程序,为了统一地访问这些物理介质,windows设计了文件系统机制.应用程序要 ...

  5. windows内核情景分析读书笔记-----HYPERSPACE

    主要介绍HYPERSPACE的创建映射函数 赏光看我这一系列文章的朋友最好结合毛德操老师的书来看,具体的细节我这里就不阐述了 简单说下这个函数功能 Windows内核有时候需要把某些物理页面临时映射到 ...

  6. Windows内核情景分析-概述

    现在的Windows 现在的windows内核包含了两大部分,一部分是本来意面上的操作系统内核,另一部分则是移到了内核中的视窗服务,前者对应ntoskrnl.exe后者win32k.sys:后者部分为 ...

  7. windows内核情景分析---进程线程2

    二.线程调度与切换 众所周知:Windows系统是一个分时抢占式系统,分时指每个线程分配时间片,抢占指时间片到期前,中途可以被其他更高优先级的线程强制抢占. 背景知识:每个cpu都有一个TSS,叫'任 ...

  8. windows内核情景分析---进程线程1

    本篇主要讲述进程的启动过程.线程的调度与切换.进程挂靠 一.进程的启动过程: BOOL CreateProcess ( LPCTSTR lpApplicationName,               ...

  9. Windows内核情景分析 笔记

    803页:WDK文档强调IoRegisterDriverReinitialization 主要用于同时支持Non-PNP和PNP下层的驱动.大概原因是:只依赖Legacy下层的驱动可以通过LoadOr ...

最新文章

  1. 如果有大型 Web 应用程序,可考虑执行预批编译
  2. 深度学习在自然语言处理研究上的进展
  3. Linux 动态库的显示调用
  4. flink的print()函数输出的都是对象地址而非对象内容
  5. 计算机网络分代核心的属性,高职单招计算机类模拟试卷二(环职职专)
  6. 【李宏毅机器学习】Basic Concept 基础概念(p4) 学习笔记
  7. linux 二进制转十进制脚本,linux-shell 脚本转换 十六进制 十进制 八进制 二进制...
  8. 2017.9.2 最大半联通子图 思考记录
  9. 数据结构之基于Java的二叉树实现
  10. python处理文本
  11. ADFS部署过程中设置network service对证书的读取权限
  12. 【Visual Studio Code 】使用Visual Studio Code + Node.js搭建TypeScript开发环境
  13. 【小白学前端】化腐朽为神奇-Bootstrap实现表单美化(day02-6)
  14. 微信小程序开发——上传图片
  15. Flash:Flash动画设计案例集合(广告条制作/遮罩动画/扫光动画/书法手写遮罩动画)图文教程之详细攻略
  16. 干活,分享!!三套简单有趣的后台登录页面模板分享
  17. 深度报道 | 瀚高软件CTO郑晓军:以开源之路发展国产数据库符合市场规律
  18. 手把手带你撸一个校园APP(四):APP功能设计及主页面框架
  19. trove mysql 镜像_OpenStack(Queens)制作 Trove 镜像
  20. 应用层——HTTP协议

热门文章

  1. 推荐算法的多模型融合
  2. 流畅的python第十四章可迭代的对象,迭代器和生成器学习记录
  3. 微信小程序进度条组件自定义数字_微信小程序之圆形进度条(自定义组件)
  4. ERP系统是什么意思?ERP系统有哪些品牌?
  5. excel2019数字太长无法显示解决办法
  6. 批量转换labelme标记为掩膜图片
  7. 常数乘以无穷大等于多少_请教一个数学问题:无穷大乘以无穷小等于多少?
  8. latex 参考文献显示问号_UESTC 本科Latex毕设论文模板 无痛上手指南
  9. 无法访问计算机无效的语法,您输入的表达式包含无效语法(The expression you entered contains invalid syntax)...
  10. 设计的界面如下图所示:窗体的标题栏显示“模拟计算器—江海大”,1个文本框用于显示输入字符和计算结果;20个按钮控件作为字符输入按键或者功能按键。