windows内核原理分析之DPC函数的执行(1)

windows内核原理分析之DPC函数的执行(1)

当发生中断时,有些操作本来应该在中断服务程序中完成,但是实际上却是在退出中断服务程序之后在一个DPC函数中完成的。DPC是”Deferred Procedure Call”的缩写,意为推迟了的过程(函数)调用。这是因为,逻辑上应该放在中断服务程序中完成的操作并非都是那么紧迫,其中有一部分可能相对而言不那么紧迫,而又比较费时间,实际上可以放在开中断的条件下执行。如果把这些操作都放在中断服务程序中,就会使关闭中断的时间太长而引起中断请求的丢失,因为整个中断服务程序通常都是在关中断的条件下执行的。为此,把中断服务程序中不那么紧迫却比较费时,而又不必在关中断条件下执行的操作分割出来,放在另一个函数中,在开中断的条件下加以执行,就可以缩短关中断的时间。这样的函数就是DPC函数。一般而言,中断服务前期的操作是比较紧迫的,并且是必须关中断的,此时可以很快地对外部设备进行操作。此后,剩下的那部分操作便可以稍后在开中断的条件下执行。所以有人曾经把这部分操作称为中断服务的”后半(Bottom Half)”,也有人把这两半分别称为”硬中断”和”软中断”。之所以要把中断服务分成前后两半,是因为一次中断服务的后半不如另一次中断的前半那么紧迫。

为此,内核中要有个DPC请求队列,中断服务程序执行完它的”前半”之后就把一个DPC请求挂入这个队列,要求内核调用相应的DPC函数,然后(形式上)就从中断返回了。接着,如果没有别的中断请求,内核就会扫描这个DPC请求队列,依次在开中断的条件下执行这些DPC函数,直至又发生中断或执行完队列中的所有DPC函数。至于当前线程所要执行的程序,则只有在DPC请求队列为空的时候才会继续得到执行。显然,这里所体现的是”急事急办”的原则,中断是最急的,DPC函数其次,最后才是当前线程。Windows内核的IRQL(即运行级别)就反映了这些活动的轻重缓急,DPC函数是在DISPATCH_LEVEL级别上执行的。

与DPC函数的执行有关的另一个问题是堆栈的使用。我们知道,中断服务程序所使用的堆栈就是当前线程的系统空间堆栈。中断服务程序一般都是比较轻小的,占用一下当前线程的堆栈不至于会有问题;但是DPC函数就不同了,DPC函数有可能是比较大的,如果仍旧占用当前线程的堆栈,在最坏的情况下有可能造成堆栈溢出,所以最好是为DPC函数的执行另外配备一个堆栈。

Windows内核把DPC请求队列放在每个CPU的PRCB数据结构中。此外,PRCB结构中还有一些别的与DPC有关的字段:

typedef struct _KPRCB  
{  
    ......  
    ULONG DpcTime;  
    ULONG DebugDpcTime;  
    ULONG InterruptTime;  
    ULONG AdjustDpcThreshold;  
    ......  
    struct _KDPC_DATA DpcData[2];        //两个DPC请求队列  
    PVOID DpcStack;  
    ULONG MaximumDpcQueueDepth;  
    ULONG DpcRequestRate;  
    ULONG MinimumDpcRate;  
    volatile UCHAR DpcInterruptRequested;  
    volatile UCHAR DpcThreadRequested;  
    volatile UCHAR DpcRoutineActive;  
    volatile UCHAR DpcThreadActive;  
    ULONG PrcbLock;  
    ULONG DpcLastCount;  
    ......  
    PVOID DpcThread;  
    KEVENT DpcEvent;  
    UCHAR ThreadDpcEnable;  
    ......  
    LONG DpcSetEventRequest;  
    ......  
    KDPC CallDpc;  
    ......  
} KPRCB, *PKPRCB; 

其中的DpcData[2]是大小为2的_KDPC_DATA结构数组,其结构定义如下:

typedef struct _KDPC_DATA  
{  
    LIST_ENTRY DpcListHead;      //用于DPC请求队列的队列头  
    ULONG DpcLock;  
    volatile ULONG DpcQueueDepth;  
    ULONG DpcCount;  
} KDPC_DATA, *PKDPC_DATA; 

可见,每个_KDPC_DATA结构中都有个队列头,这就是用于DPC请求队列的。

DpcData[]的大小为2,说明有两个DPC请求队列,它们是:

#define DPC_NORMAL      0  
#define DPC_THREADED    1 

显然,其中之一是”常规”的DPC请求队列,另一个是”线程化”的DPC请求队列。目前ReactOS已经实现的是常规DPC请求。

设备对象的数据结构DEVICE_OBJECT中有个成分Dpc,这是个KDPC数据结构,用来设置有关本设备对象的DPC函数的信息:

typedef struct _KDPC  
{  
    UCHAR Type;                             //DpcObject或ThreadedDpcObject  
    UCHAR Importance;                       //紧迫程度  
    USHORT Number;                          //CPU号码(在多处理器系统中)  
    LIST_ENTRY DpcListEntry;                //用来挂入DPC请求队列  
    PKDEFERRED_ROUTINE DeferredRoutine;     //指向具体的DPC函数  
    PVOID DeferredContext;                  //执行DPC函数时的上下文  
    PVOID SystemArgument1;                  //执行DPC函数时的参数  
    PVOID SystemArgument2;  
    volatile PVOID  DpcData;                //指向所挂入的KDPC_DATA结构  
} KDPC, *PKDPC, *RESTRICTED_POINTER PRKDPC; 

设备对象在初始化时通过KeInitializeDpc()设置好它的KDPC数据结构。

VOID NTAPI  
KeInitializeThreadedDpc(IN PKDPC Dpc, IN 
PKDEFERRED_ROUTINE DeferredRoutine,  
                      IN PVOID DeferredContext)  
{  
    /* Call the internal routine */  
    KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, 
ThreadedDpcObject);  
}  

VOID NTAPI  
KiInitializeDpc(IN PKDPC Dpc, IN PKDEFERRED_ROUTINE DeferredRoutine,  
              IN PVOID DeferredContext, IN KOBJECTS Type)  
{  
    /* Setup the DPC Object */  
    Dpc->TypeType = Type;  
    Dpc->Number = 0;  
    Dpc->Importance= MediumImportance;  
    Dpc->DeferredRoutineDeferredRoutine = DeferredRoutine;  
    Dpc->DeferredContextDeferredContext = DeferredContext;  
    Dpc->DpcData = NULL;  
} 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值