您的位置:首页 > 其它

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

2017-09-04 11:46 423 查看
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时钟,在迫不得已的情况下使用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: