AutoCAD 3DMAX C语言 Pro/E UG JAVA编程 PHP编程 Maya动画 Matlab应用 Android
Photoshop Word Excel flash VB编程 VC编程 Coreldraw SolidWorks A Designer Unity3D
 首页 > VC编程

Win32 核心 DPC 设计思想和实现思路浅析

51自学网 2015-08-30 http://www.wanshiok.com

 

  KeInsertQueueDpc函数(ntos/ke/dpcobj.c:89)实际上是系统对DPC队列维护的核心函数,其伪代码如下:


以下为引用:

BOOLEAN KeInsertQueueDpc (IN PRKDPC Dpc, IN PVOID SystemArgument1,IN PVOID SystemArgument2)
{
 PKSPIN_LOCK Lock;
 KIRQL OldIrql;

 KeRaiseIrql(HIGH_LEVEL, &OldIrql); // 提升当前IRQL到最高,屏蔽其它中断

 PKPRCB = KeGetCurrentPrcb();    // 获取当前处理器控制块

 // 通过比较Dpc->Lock是否为空,来判断此DPC对象是否已经被加入到DPC队列;
 // 如果DPC对象可以被加入到队列,则将当前处理器控制块的DPC自旋锁复制到Dpc->Lock中
 if ((Lock = InterlockedCompareExchangePointer(&Dpc->Lock, &Prcb->DpcLock, NULL)) == NULL)
 {
  // 更新当前处理器控制块的统计信息
  Prcb->DpcCount += 1;
  Prcb->DpcQueueDepth += 1;

  // 更新DPC对象的参数信息
  Dpc->SystemArgument1 = SystemArgument1;
  Dpc->SystemArgument2 = SystemArgument2;

  // 根据DPC对象优先级,决定将之加入到DPC队列的头部或尾部
  if (Dpc->Importance == HighImportance)
    InsertHeadList(&Prcb->DpcListHead, &Dpc->DpcListEntry);
  else
    InsertTailList(&Prcb->DpcListHead, &Dpc->DpcListEntry);

  // 如果当前处理器没有DPC对象活动或DPC中断请求,则进一步判断是否发出DPC中断请求
  if (Prcb->DpcRoutineActive == FALSE && Prcb->DpcInterruptRequested == FALSE)
  {
   // 如果DPC对象优先级为中高;
   // 或者DPC队列长度超过阈值MaximumDpcQueueDepth;
   // 或者DPC请求速率小于阈值MinimumDpcRate
   if ((Dpc->Importance != LowImportance) ||
     (Prcb->DpcQueueDepth >= Prcb->MaximumDpcQueueDepth) ||
     (Prcb->DpcRequestRate < Prcb->MinimumDpcRate))
   {
    // 满足触发条件,则发出DPC中断请求
    Prcb->DpcInterruptRequested = TRUE;
    KiRequestSoftwareInterrupt(DISPATCH_LEVEL);
   }
  }
 }
 KeLowerIrql(OldIrql);
 return (Lock == NULL);
}




  这里的几个阈值,在KiInitializeKernel函数(ntos/ke/i386/kernlini.c:246)中,根据全局变量KiMaximumDpcQueueDepth、KiMinimumDpcRate和KiAdjustDpcThreshold确定。而这几个全局变量可以通过注册表项(HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/kernel/)下的DpcQueueDepth、MinimumDpcRate和AdjustDpcThreshold三个键值来设置。具体的设置方法,请参考MSDN以及性能计数器的Processor/% DPC Time等动态指数。

  而处理与驱动绑定的DPC对象的IoRequestDpc函数只是KeInsertQueueDpc函数的一个简单包装。


以下为引用:

#define IoRequestDpc( DeviceObject, Irp, Context ) ( /
  KeInsertQueueDpc( &(DeviceObject)->Dpc, (Irp), (Context) ) )




  与KeInsertQueueDpc函数对应的KeRemoveQueueDpc函数(ntos/ke/dpcobj.c:272)实际上只是完成简单的将DPC对象从DPC队列中删除的功能。

  最后对DPC对象属性进行修改的KeSetImportanceDpc函数(ntos/ke/dpcobj.c:367)和KeSetTargetProcessorDpc函数(ntos/ke/dpcobj.c:401)实际上都是直接修改DPC对象结构的相应域。KDPC::Number大于MAXIMUM_PROCESSORS = 32时,用于指定DPC对象的目标CPU。如调用KeSetTargetProcessorDpc(pKDpc, 2)后,pKDpc = MAXIMUM_PROCESSORS + 2。

  在了解了DPC对象和DPC队列的大致维护函数功能后,我们来看看稍微复杂一些的在多处理器下DPC队列的维护流程。

  前面提到KDPC::Number指定了DPC对象所用的处理器号,因此在KeInsertQueueDpc函数开始获取处理器控制块时,需要判断Number是否指向一个处理器,并从全局处理器控制块列表中获取相应的处理器控制块,为代码如下:


以下为引用:

if (Dpc->Number >= MAXIMUM_PROCESSORS) // Number大于MAXIMUM_PROCESSORS时用于指定处理器
{
 Processor = Dpc->Number - MAXIMUM_PROCESSORS;
 Prcb = KiProcessorBlock[Processor];  // 全局唯一的处理器控制块列表

}
else
{
 Prcb = KeGetCurrentPrcb();
}

KiAcquireSpinLock(&Prcb->DpcLock);   // 使用自旋锁保护处理器控制块中的DPC队列




  而在KeInsertQueueDpc函数中判断是否发出DPC中断请求时,也需要做更复杂的逻辑判断。
  对DPC对象目标处理器就是当前处理器的情况,可以和前面单处理器时一样处理,直接发送DPC中断请求;但对于DPC对象目标处理器是其他处理器的情况,就必须使用KiIpiSend函数发送IPI(InterProcessor Interrupt)中断,通知目标处理器执行动作。此IPI中断是介于系统掉电中断(POWER_LEVEL)和时钟中断之间的特殊IRQL,专门用于在多处理器情况下协调多个处理器的工作。
  此外就是在多处理器情况下,各种对DPC队列的操作都需要用此处理器控制块的DPC队列自旋锁保护起来,避免同步问题。

  由此我们可以看到,实际上DPC队列是每个处理器一个的,我们完全可以将某个DPC对象绑定到某个处理器上,实现类似线程亲缘性(Thread Affinity)的效果,优化在多处理器环境下的性能。但这同时也带来一个问题,就是ISR程序可以和DPC回调函数同时被调用,某种程度上也造成了开发复杂度的增加,具体处理方法请参考DDK中相关文档。

  Kernel-Mode Driver Architecture/Design Guide/Servicing Interrupts/DPC Objects and DPCs

 
 
说明
:本教程来源互联网或网友上传或出版商,仅为学习研究或媒体推广,wanshiok.com不保证资料的完整性。

上一篇:利用钩子实现菜单阴影效果  下一篇:VC++的链接错误LNK2001