Windows内核源码详尽分析-DPC篇
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
DPC
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)
{