DPC分析 基于ReactOS0.33

    windows的过程,有如linux的软中断。以前Linux内核中自旋锁同步分析提到过,Linux通过IN_HARDIRQ/IN_SOFTIRQ来屏蔽软中断的执行,windows其实也类似,偷梁换柱的用IRQL来代替这些概念:IRQL高于DPC_LEVEL时,DPC过程无法执行,因为可能是中断过程正在执行;当通过KfLowerIrql降低IRQL请求级别到DPC_LEVEL及以下时开始执行DPC过程。降的再狠点,降到APC_LEVEL之下,还能执行APC请求。

    ReactOS中关于DPC的执行流程相比与其他模块极其简单,因此本文只罗列大概流程,并不贴出代码。首先关于DPC对象的产生,设备对象初始化时可能会调用KiInitializeDpc,该函数仅仅设置执行DPC的过程和相应的参数,然后就结束了。设备对象可能会把该Dpc对象保存在自己的设备扩展部中,供以后使用。以后是指什么?当然是指发生中断而进入中断处理函数时,会调用KeInsertQueueDpc把Dpc对象插入到CPU控制模块Prcb!DpcData队列:

KDPC对象

typedef struct _KDPC
{
    UCHAR Type;
    UCHAR Importance;
    USHORT Number;
    LIST_ENTRY DpcListEntry;
    PKDEFERRED_ROUTINE DeferredRoutine;
    PVOID DeferredContext;
    PVOID SystemArgument1;
    PVOID SystemArgument2;
	//指向所挂入的KDPC_DATA结构,Prcb
	//上有两个KDPC_DATA(DPC)请求队列,一个是
	//普通的一个是DPC线程化的
    volatile PVOID  DpcData;
} KDPC, *PKDPC, *RESTRICTED_POINTER PRKDPC;
KeInsertQueueDpc代码片段:

KeRaiseIrql(HIGH_LEVEL, &OldIrql);
CurrentPrcb = KeGetCurrentPrcb();//获得当前CPU的Prcb
...
/*
    从Prcb的两个DpcData队列中选一个,将请求的Dpc挂到其中
    */
    /* Check if this is a threaded DPC and threaded DPCs are enabled */
    if ((Dpc->Type == ThreadedDpcObject) && (Prcb->ThreadDpcEnable))
    {
        /* Then use the threaded data */
        DpcData = &Prcb->DpcData[DPC_THREADED];
    }
    else
    {
        /* Otherwise, use the regular data */
        DpcData = &Prcb->DpcData[DPC_NORMAL];
    }
...
KiAcquireSpinLock(&DpcData->DpcLock);//同个CPU的互斥,因为要插入队列
..
Dpc->SystemArgument1 = SystemArgument1;
        Dpc->SystemArgument2 = SystemArgument2;
        DpcData->DpcQueueDepth++;
        DpcData->DpcCount++;
        DpcConfigured = TRUE;

        /* Check if this is a high importance DPC */
        if (Dpc->Importance == HighImportance)
        {
            /* Pre-empty other DPCs */
            InsertHeadList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
        }
        else
        {
            /* Add it at the end */
            InsertTailList(&DpcData->DpcListHead, &Dpc->DpcListEntry);
        }
//插入队列结束,释放互斥锁
KiReleaseSpinLock(&DpcData->DpcLock);
...
//最后发出DPC请求,当然DPC不会立马得到执行
/*
            正式发出请求,等到cpu从高IRQL降到DPC以下时,
            内核扫描并执行队列中的DPC函数,如处理完
            中断后调用KfLowerIrql时,据说扫描并执行DPC队列
            也是线程切换的时机,这么说KiSwapContext是在DPC级别的
            切换完了,IRQL再降级,才有机会执行APC
            */
            HalRequestSoftwareInterrupt(DISPATCH_LEVEL);
DPC的产生和请求都有了,那什么时候才是投递执行的时候?前面说了要等到IRQL下降,一般由调用者主动调用KfLowerIrql降低IRQL,借此机会DPC过程得以执行.

KfLowerIrql的内部实现是HalpLowerIrql,所以有必要进去浏览一下:

VOID 
HalpLowerIrql(KIRQL NewIrql, BOOLEAN FromHalEndSystemInterrupt)
{
//执行Dpc/线程切换/Apc/的大好时机
  ULONG Flags;
  UCHAR DpcRequested;
  if (NewIrql >= DISPATCH_LEVEL)
    {
      KeSetCurrentIrql (NewIrql);
      APICWrite(APIC_TPR, IRQL2TPR (NewIrql) & APIC_TPR_PRI);
      return;
    }
  Ke386SaveFlags(Flags);
  if (KeGetCurrentIrql() > APC_LEVEL)
    {
      KeSetCurrentIrql (DISPATCH_LEVEL);
      APICWrite(APIC_TPR, IRQL2TPR (DISPATCH_LEVEL) & APIC_TPR_PRI);
      DpcRequested = __readfsbyte(FIELD_OFFSET(KIPCR, HalReserved[HAL_DPC_REQUEST]));
      if (FromHalEndSystemInterrupt || DpcRequested)
        {
        	//以前在中断处理中发出的Dpc请求,现在要关闭
          __writefsbyte(FIELD_OFFSET(KIPCR, HalReserved[HAL_DPC_REQUEST]), 0);
          _enable();
		  //投递Dpc
          KiDispatchInterrupt();
          if (!(Flags & EFLAGS_INTERRUPT_MASK))
            {
              _disable();
            }
	}
      KeSetCurrentIrql (APC_LEVEL);
    }
  if (NewIrql == APC_LEVEL)
    {
      return;
    }
  if (KeGetCurrentThread () != NULL && 
      KeGetCurrentThread ()->ApcState.KernelApcPending)
    {
    	//投递APC
      _enable();
      KiDeliverApc(KernelMode, NULL, NULL);
      if (!(Flags & EFLAGS_INTERRUPT_MASK))
        {
          _disable();
        }
    }
  KeSetCurrentIrql (PASSIVE_LEVEL);
}
投递Dpc还是在KiDispatchInterrupt中完成。这里有个疑问,希望路过的大神解答就是:ReactOS处理中断用了线程的堆栈,而处理Dpc使用Dpc专用堆栈,据说说linux中断处理过程没有进程的上下文,莫非linux中断用的是tss中设置的esp0?

这个函数,除了执行dpc还有一个重要的作用,切换线程

call @KiRetireDpcList@4
...
call @KiSwapContextInternal@0
以前一直不知道线程切换是在哪个IRQL级别上,这回算是知道了~

最后KiRetireDpcList函数的实现很简单,就是如果有Dpc请求就遍历Prcb的Dpc队列,并执行其中的DPC过程。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值