DPC,时钟中断,以及DPC定时器(3)

来源:互联网 发布:社交媒体数据分析招聘 编辑:程序博客网 时间:2024/04/29 16:25

3.定时器的扫描过程

真正开始扫描定时器的就是上述的KiRetireDpcList 检测到PRCB的定时器请求是true 调用KiTimerExpiration

//解除定时器的操作VOIDKiTimerExpiration (    IN PKDPC TimerDpc,    IN PVOID DeferredContext,    IN PVOID SystemArgument1,    IN PVOID SystemArgument2    )/*++Routine Description:    This function is called when the clock interupt routine discovers that    a timer has expired.    N.B. This function executes on the same processor that receives clock         interrupts.Arguments:    TimerDpc - Not used.    DeferredContext - Not used.    SystemArgument1 - Supplies the starting timer table index value to        use for the timer table scan.    SystemArgument2 - Not used.Return Value:    None.--*/{    ULARGE_INTEGER CurrentTime;    ULONG DpcCount;    PKDPC Dpc;    DPC_ENTRY DpcTable[MAXIMUM_TIMERS_PROCESSED];    KIRQL DummyIrql;    LONG HandLimit;    LONG Index;    LARGE_INTEGER Interval;    PLIST_ENTRY ListHead;    PKSPIN_LOCK_QUEUE LockQueue;    PLIST_ENTRY NextEntry;    KIRQL OldIrql;    LONG Period;#if !defined(NT_UP) || defined(_WIN64)    PKPRCB Prcb = KeGetCurrentPrcb();#endif    ULARGE_INTEGER SystemTime;    PKTIMER Timer;    ULONG TimersExamined;    ULONG TimersProcessed;    UNREFERENCED_PARAMETER(TimerDpc);    UNREFERENCED_PARAMETER(DeferredContext);    UNREFERENCED_PARAMETER(SystemArgument2);    //    // Capture the timer expiration time, the current interrupt time, and    // the low tick count.    //    // N.B. Interrupts are disabled to ensure that interrupt activity on the    //      current processor does not cause the values read to be skewed.    //    _disable();    KiQuerySystemTime((PLARGE_INTEGER)&SystemTime);    KiQueryInterruptTime((PLARGE_INTEGER)&CurrentTime);    HandLimit = (LONG)KiQueryLowTickCount();    _enable();    //    // If the timer table has not wrapped, then start with the specified    // timer table index value, and scan for timer entries that have expired.    // Otherwise, start with the specified timer table index value and scan    // the entire table for timer entries that have expired.    //    // N.B. This later condition exists when DPC processing is blocked for a    //      period longer than one round trip throught the timer table.    //    // N.B. The current instance of the timer expiration execution will only    //      process the timer queue entries specified by the computed index    //      and hand limit. If another timer expires while the current scan    //      is in progress, then another scan will occur when the current one    //      is finished.    //    Index = PtrToLong(SystemArgument1);       //时钟中断时的滴答数    if ((ULONG)(HandLimit - Index) >= TIMER_TABLE_SIZE) { //如果这两个滴答数的差距已经超过了TIMER_TABLE_SIZE,最大值就是Index + TIMER_TABLE_SIZE - 1了        HandLimit = Index + TIMER_TABLE_SIZE - 1;       }    Index -= 1;    HandLimit &= (TIMER_TABLE_SIZE - 1);       //当前滴答数 取余 HandLimit是指遍历的下标最多不超过某个值    //    // Acquire the dispatcher database lock and read the current interrupt    // time to determine which timers have expired.    //    DpcCount = 0;    TimersExamined = MAXIMUM_TIMERS_EXAMINED;      //每次缓存表 尝试释放的定时器数    TimersProcessed = MAXIMUM_TIMERS_PROCESSED;     //每次缓存表 可执行的最多定时器数,这个上限满了之后就会集中处理这批DPC    KiLockDispatcherDatabase(&OldIrql);    do {        Index = (Index + 1) & (TIMER_TABLE_SIZE - 1);   //从当前的Index开始 每个 取余得到下标        ListHead = &KiTimerTableListHead[Index].Entry;        while (ListHead != ListHead->Flink) {     //链表循环            LockQueue = KiAcquireTimerTableLock(Index);            NextEntry = ListHead->Flink;            Timer = CONTAINING_RECORD(NextEntry, KTIMER, TimerListEntry);            TimersExamined -= 1; //遍历一个定时器这个就-1            if ((NextEntry != ListHead) &&                   (Timer->DueTime.QuadPart <= CurrentTime.QuadPart)) { //timer的时间小于当前中断时间 符合条件,实际比较的一直是中断时间                //                // The next timer in the current timer list has expired.                // Remove the entry from the timer tree and set the signal                // state of the timer.                //                TimersProcessed -= 1;                KiRemoveEntryTimer(Timer);                Timer->Header.Inserted = FALSE;                KiReleaseTimerTableLock(LockQueue);                Timer->Header.SignalState = 1;                //                // Capture the DPC and period fields from the timer object.                // Once wait test is called, the timer must not be touched                // again unless it is periodic. The reason for this is that                // a thread may allocate a timer on its local stack and wait                // on it. Wait test can cause that thread to immediately                // start running on another processor on an MP system. If                // the thread returns, then the timer will be corrupted.                //                         Dpc = Timer->Dpc;                Period = Timer->Period;          //如果有等待这个定时器的线程,尝试释放                if (IsListEmpty(&Timer->Header.WaitListHead) == FALSE) {                    if (Timer->Header.Type == TimerNotificationObject) {                        KiWaitTestWithoutSideEffects(Timer, TIMER_EXPIRE_INCREMENT);                    } else {                        KiWaitTestSynchronizationObject(Timer, TIMER_EXPIRE_INCREMENT);                    }                }                //                // If the timer is periodic, then compute the next interval                // time and reinsert the timer in the timer tree.                //                // N.B. Even though the timer insertion is relative, it can                //      still fail if the period of the timer elapses in                //      between computing the time and inserting the timer.                //      If this happens, then the insertion is retried.                //                if (Period != 0) {           //如果是循环定时器 在把它插入表中                    Interval.QuadPart = Int32x32To64(Period, - 10 * 1000);                    do {                    } while (KiInsertTreeTimer(Timer, Interval) == FALSE);                }                //                // If a DPC is specified, then insert it in the target                // processor's DPC queue or capture the parameters in                // the DPC table for subsequent execution on the current                // processor.                //                if (Dpc != NULL) {#if defined(NT_UP)      //单核                    DpcTable[DpcCount].Dpc = Dpc; //局部缓存的DPC数组                     DpcTable[DpcCount].Routine = Dpc->DeferredRoutine;                    DpcTable[DpcCount].Context = Dpc->DeferredContext;                    DpcCount += 1;#else      //多核要判断指定的cpu 如果是自己的CPU或者没有指定,放入缓存表中就好,否则插入对应CPU的DPC链表中                    if (((Dpc->Number >= MAXIMUM_PROCESSORS) &&                         (((LONG)Dpc->Number - MAXIMUM_PROCESSORS) != Prcb->Number)) ||                        ((Dpc->Type == (UCHAR)ThreadedDpcObject) &&                         (Prcb->ThreadDpcEnable != FALSE))) {                        KeInsertQueueDpc(Dpc,       //内部的一个缓存表                                         ULongToPtr(SystemTime.LowPart),                                         ULongToPtr(SystemTime.HighPart));                            } else {                        DpcTable[DpcCount].Dpc = Dpc;                        DpcTable[DpcCount].Routine = Dpc->DeferredRoutine;                        DpcTable[DpcCount].Context = Dpc->DeferredContext;                        DpcCount += 1;                    }#endif                }                //                // If the maximum number of timers have been processed or                // the maximum number of timers have been examined, then                // drop the dispatcher lock and process the DPC table.                //                if ((TimersProcessed == 0) || (TimersExamined == 0)) { //如果这两个上限已经完了,但还是有没搞定的DPC 调用KiProcessTimerDpcTable                   //处理DpcTable                    KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], DpcCount);                    //                    // Initialize the DPC count, the scan counters, and                    // acquire the dispatcher database lock.                    //                    // N.B. Control is returned with the dispatcher database                    //      unlocked.                    //                    DpcCount = 0;                    TimersExamined = MAXIMUM_TIMERS_EXAMINED;                    TimersProcessed = MAXIMUM_TIMERS_PROCESSED;#if defined(_WIN64)                    if (KiTryToLockDispatcherDatabase(&DummyIrql) == FALSE) {                        Prcb->TimerHand = 0x100000000I64 + Index;                        return;                    }#else                    KiLockDispatcherDatabase(&DummyIrql);//FIXME 啥意思?#endif                }            } else {//走到这里表示链表遍历结束或者链表里面的后半部分已经比现在系统时间大了,从大的第一个timer变成链表头 并且哈希表的due time也被更新                //                // If the timer table list is not empty, then set the due time                // to the first entry in the respective timer table entry.                //                // N.B. On the x86 the write of the due time is not atomic.                //      Therefore, interrupts must be disabled to synchronize                //      with the clock interrupt so the clock interupt code                //      does not observe a partial update of the due time.                //                // Release the timer table lock.                //                if (NextEntry != ListHead) {                    ASSERT(KiTimerTableListHead[Index].Time.QuadPart <= Timer->DueTime.QuadPart);#if defined(_X86_)                    _disable();                    KiTimerTableListHead[Index].Time.QuadPart = Timer->DueTime.QuadPart;                    _enable();#else                    KiTimerTableListHead[Index].Time.QuadPart = Timer->DueTime.QuadPart;#endif                }                KiReleaseTimerTableLock(LockQueue);                //                // If the maximum number of timers have been scanned, then                // drop the dispatcher lock and process the DPC table.                //     //这里也要测试一下是否要执行DPC                if (TimersExamined == 0) {                    KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], DpcCount);                    //                    // Initialize the DPC count, the scan counters, and                    // acquire the dispatcher database lock.                    //                    // N.B. Control is returned with the dispatcher database                    //      unlocked.                    //                    DpcCount = 0;                    TimersExamined = MAXIMUM_TIMERS_EXAMINED;                    TimersProcessed = MAXIMUM_TIMERS_PROCESSED;#if defined(_WIN64)                    if (KiTryToLockDispatcherDatabase(&DummyIrql) == FALSE) {                        Prcb->TimerHand = 0x100000000I64 + Index;                        return;                    }#else                    KiLockDispatcherDatabase(&DummyIrql);#endif                }                break;//既然已经超了 可以退出了            }        }    } while(Index != HandLimit);#if DBG    if (KeNumberProcessors == 1) {        KiCheckTimerTable(CurrentTime);    }#endif    //    // If the DPC table is not empty, then process the remaining DPC table    // entries and lower IRQL. Otherwise, unlock the dispatcher database.    //    // N.B. Control is returned from the DPC processing routine with the    //      dispatcher database unlocked.    ////最后可能还有剩余的DPC 处理掉    if (DpcCount != 0) {        KiProcessTimerDpcTable(&SystemTime, &DpcTable[0], DpcCount);        if (OldIrql != DISPATCH_LEVEL) {            KeLowerIrql(OldIrql);        }    } else {        KiUnlockDispatcherDatabase(OldIrql);    }    return; }

这个函数获取时钟中断时的滴答数和现在的滴答数,算出相应的hand范围,然后遍历Index到HandLimit之间的链表,如果链表中的duetime小于等于当前的中断时间,就把dpc放入缓存表中处理。

如果DPC指明了一个运行的CPU,就把这个DPC作为普通的DPC插入到相应的DPC链表中处理

至此,DPC定时器的工作原理算是清楚了。

参考: 系统时钟中断服务例程的建立

         时钟中断与线程调度小计

其他参考同以前几篇文章


原创粉丝点击