DPCDPC不同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;//挂入时的系统附加参数1PVOID SystemArgument2;//挂入时的系统附加参数2volatile PVOID DpcData;//所在的dpc队列} KDPC, *PKDPC, *RESTRICTED_POINTER PRKDPC;VOIDKeInitializeDpc(IN PKDPC Dpc,//DPC对象(DPC也是一种内核对象)IN PKDEFERRED_ROUTINE DeferredRoutine, //DPC函数IN PVOID DeferredContext)//DPC函数的参数{KiInitializeDpc(Dpc, DeferredRoutine, DeferredContext, DpcObject);}VOIDKiInitializeDpc(IN PKDPC Dpc,IN PKDEFERRED_ROUTINE DeferredRoutine,IN PVOID DeferredContext,IN KOBJECTS Type){Dpc->Type = Type;Dpc->Number = 0;//初始的目标cpu为当前cpuDpc->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队列elseDpcData = &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);elseInsertTailList(&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);elseHalRequestSoftwareInterrupt(DISPATCH_LEVEL);//给当前cpu发出一个dpc软中断}KeLowerIrql(OldIrql);//降低irqlreturn DpcConfigured;}如上,这个函数将dpc挂入目标cpu的指定dpc队列(每个cpu有两个dpc队列,一个普通的,一个线程化的)。
然后检查当前是否可以立即向目标cpu发出一个dpc软中断,这样,以在下次降低到DISPATCH_LEVEL 以下时扫描执行dpc(其实上面的这个函数一般用于在isr中调用,但用户也可以随时手动调用)(一般来说,挂入的都是中等重要性的dpc,一般在dpc进队的同时就会顺势发出一个dpc中断.)下面的函数可用于模拟硬件,向cpu发出任意irql级别的软中断,请求cpu处理执行那种中断。
VOID FASTCALLHalRequestSoftwareInterrupt(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。