Windows 驱动机制-定时、DPC及延时

1、定义Timer及DPC
typedef struct _DEVICE_EXTENSION  {
        .
        .
        .
    KDPC            Dpc;
    KTIMER          Timer;
  
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
 
2、初始化Timer及DPC
    LARGE_INTEGER DueTime;
    DueTime.QuadPart=-10000*2000;//定时2秒
    KeInitializeDpc(&deviceExtension->Dpc,CustomTimerDPC,deviceExtension);
    KeInitializeTimer(&deviceExtension->Timer);
    KeSetTimer(&deviceExtension->Timer,DueTime,&deviceExtension->Dpc);
 
3、DPC处理函数
void CustomTimerDPC (
IN PKDPC pDpc,
IN PVOID deferredContext,
IN PVOID systemArgument1,
IN PVOID systemArgument2
) {
 PDEVICE_EXTENSION deviceExtension = NULL;
 KIRQL OldIrql;
 if (sizeof(deferredContext) != sizeof(PDEVICE_EXTENSION))
  return;
 deviceExtension = (PDEVICE_EXTENSION)deferredContext;
 SKYDbgPrint (SKY_CHECK, ("Timeout"));
}
 
4、取消定时器
KeCancelTimer(&deviceExtension->Timer);
 
注:KeSetTimer的定时精度为15ms左右,如果要更高精度的定时,请用其它方法。DPC必须为经过KeInitializeDpc初始化后的DPC
 
内核延时

方法一:NdisMSleep

VOID
   NdisMSleep(
    IN ULONG  MicrosecondsToSleep
    );

直接调用NdisMSleep,它的参数是微秒数量级。不过这里一定要注意

调用环境:

KeGetCurrentIrql < DISPATCH_LEVEL

方法二:NdisStallExecution

VOID
   NdisStallExecution(
    IN UINT  MicrosecondsToStall
    );

这里也是直接调用,参数是微秒级,但是最好不要用它延时超过50个微秒。

调用环境:

Any IRQL

MicrosecondsToStall <= 50

方法三:KeDelayExecutionThread

NTSTATUS 
  KeDelayExecutionThread(
     IN KPROCESSOR_MODE  WaitMode,
     IN BOOLEAN  Alertable,
     IN PLARGE_INTEGER  Interval
     );

    该函数将当前执行线程置于等待状态,当时间过后被唤醒。

调用环境:

KeGetCurrentIrql <= APC_LEVEL

参考代码:

    LARGE_INTEGER liTime;


    /* Convert milliseconds to 100-nanosecond increments using:
     *
     *     1 ns = 10 ^ -9 sec
     *   100 ns = 10 ^ -7 sec (1 timer interval)
     *     1 ms = 10 ^ -3 sec
     *     1 ms = (1 timer interval) * 10^4
     */
    delay = delay * 10000;

    // Negative value means relative time, not absolute
    liTime =
        RtlConvertLongToLargeInteger(
            -(LONG)delay
            );

    //Callers of KeDelayExecutionThread must be running at IRQL <= APC_LEVEL.
    DbgPrint("KeGetCurrentIrql = %d\n", KeGetCurrentIrql());
    KeDelayExecutionThread(
        KernelMode,
        TRUE,
        &liTime
        );
       

方法四:KeWaitForSingleObject

NTSTATUS 
  KeWaitForSingleObject(
     IN PVOID  Object,
     IN KWAIT_REASON  WaitReason,
     IN KPROCESSOR_MODE  WaitMode,
     IN BOOLEAN  Alertable,
     IN PLARGE_INTEGER  Timeout  OPTIONAL
     );

    该函数等待信号的到来,如果在所设时间之内没有信号,则返回TimeOut。

调用环境:

KeGetCurrentIrql <= PASSIVE_LEVEL

参考代码:

    LARGE_INTEGER TimeoutTimer;

     TimeoutTimer = RtlConvertLongToLargeInteger(
            -(LONG)(delay * 10000)
            );

    //sleep,waiting (TimeoutWait) singaled
    DbgPrint("KeGetCurrentIrql = %d\n", KeGetCurrentIrql());
    status = KeWaitForSingleObject(
            &TimeoutWait,
            Executive,
            KernelMode,
            FALSE,
            &TimeoutTimer
    );

方法五:空循环

调用环境:

    任何情况下都可使用。

参考代码:

    LARGE_INTEGER liTime, startTime, currentTime;


    /* Convert milliseconds to 100-nanosecond increments using:
     *
     *     1 ns = 10 ^ -9 sec
     *   100 ns = 10 ^ -7 sec (1 timer interval)
     *     1 ms = 10 ^ -3 sec
     *     1 ms = (1 timer interval) * 10^4
     */
    delay = delay * 10000;
            
    KeQuerySystemTime(&startTime);
    while(1){
       KeQuerySystemTime(¤tTime);
       liTime.QuadPart = currentTime.QuadPart - startTime.QuadPart;
       if(liTime.QuadPart >= delay) break;
    }         

    以上方法中前面四种不占用CPU时钟,第五种方法会占用CPU时钟,在迫不得已的情况下使用。

你可能感兴趣的:(驱动设计,Windows)