[关闭]
@zoand 2015-06-20T17:26:00.000000Z 字数 5892 阅读 2064

Windows WDK编程实现高精度定时

amd64驱动


通常要实现精度效低的定时,如1s的定时,在用户模式下,用for循环每次让程序Sleep(1000)即可,即让程序每1s中唤醒一次,如可以打印当前的时间,实现时钟的效果。但Sleep函数的偏差效大,用于秒级的定时还勉强可接受,但如果用Sleep函数实现1ms定时,很容易出现比1ms还大得多的偏差,因此这种方法无法实现精度效高的定时。

下面介绍一种采用WDK实现在Windows内核线程中完成高精度定时,定时效果:1ms定时误差小于0.3%,即小于3us

Windows内核线程的创建需要在驱动程序中实现。

1. 驱动入口程序DriverEntry

在驱动入口程序DriverEntry中先按照常规做法,创建设备对象,以及设置派遣例程。

  1. NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING regPath)
  2. {
  3. PDEVICE_OBJECT pDeviceObject = NULL;
  4. NTSTATUS ntStatus;
  5. UNICODE_STRING uniNtNameString, uniWin32NameString;
  6. // 创建命名的设备对象
  7. RtlInitUnicodeString( &uniNtNameString, NT_DEVICE_NAME );
  8. ntStatus = IoCreateDevice(
  9. pDriverObject,
  10. sizeof(SYSTHREAD_DEVICE_EXTENSION), // DeviceExtensionSize &uniNtNameString,
  11. FILE_DEVICE_UNKNOWN, //
  12. 0, // No standard device characteristics
  13. FALSE, // not exclusive device
  14. &pDeviceObject
  15. );
  16. if( !NT_SUCCESS(ntStatus) ) {
  17. return ntStatus;
  18. }
  19. // 派遣例程入口
  20. pDriverObject->MajorFunction[IRP_MJ_CREATE] = SysThreadOpen;
  21. pDriverObject->MajorFunction[IRP_MJ_CLOSE] = SysThreadClose;
  22. pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SysThreadDeviceIoControl;
  23. pDriverObject->DriverUnload = SysThreadUnload;
  24. pDeviceObject->Flags |= DO_BUFFERED_IO;
  25. RtlInitUnicodeString( &uniWin32NameString, DOS_DEVICE_NAME );
  26. ntStatus = IoCreateSymbolicLink( &uniWin32NameString, &uniNtNameString );
  27. if (!NT_SUCCESS(ntStatus)){
  28. IoDeleteDevice( pDriverObject->DeviceObject );
  29. }
  30. return ntStatus;
  31. }

2. 设备IO控制例程

在该例程中处理开启线程的关闭线程的请求,以便当加载并启动该驱动后,可以在用户级别的应用程序中,向该驱动设备发送开启线程和关闭线程的请求,来完成内核线程的创建的销毁。
该例程中,pdx是一个 PSYSTHREAD_DEVICE_EXTENSION的变量, 指向设备对象的 DeviceExtension域,该域是一个由程序员自己定义大小和数据结构的内存空间,程序中为该域定义了一种结构体 SYSTHREAD_DEVICE_EXTENSION,在DriverEntry中创建设备对象时,指定了 DeviceExtension域的大小为sizeof( SYSTHREAD_DEVICE_EXTENSION)。
用pdx指向该域,并将其传给内核线程,用该结构体中的KEVENT变量evKill来向实现线程的同步,内核线程会轮询该变量,在需要关闭内核线程的时,通过设置该变量通知内核结束运行。

  1. typedef struct
  2. _SYSTHREAD_DEVICE_EXTENSION
  3. {
  4. KEVENT evKill;
  5. PKTHREAD thread;
  6. } SYSTHREAD_DEVICE_EXTENSION, *PSYSTHREAD_DEVICE_EXTENSION;
  7. NTSTATUS SysThreadDeviceIoControl(I N PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp )
  8. {
  9. NTSTATUS ntStatus = STATUS_SUCCESS;
  10. PIO_STACK_LOCATION pIrpStack;
  11. PSYSTHREAD_DEVICE_EXTENSION pdx;
  12. ULONG dwControlCode;
  13. pdx = (PSYSTHREAD_DEVICE_EXTENSION) pDeviceObject->DeviceExtension;
  14. pIrpStack = IoGetCurrentIrpStackLocation( pIrp );
  15. dwControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;
  16. switch(dwControlCode)
  17. {
  18. case IOCTL_SYSTHREAD_START:
  19. StartThread(pdx); //线程开始
  20. break;
  21. case IOCTL_SYSTHREAD_STOP:
  22. StopThread(pdx); //线程结束
  23. break;
  24. default:
  25. break;
  26. }
  27. pIrp->IoStatus.Status = STATUS_SUCCESS;
  28. pIrp->IoStatus.Information = 0;
  29. IoCompleteRequest( pIrp, IO_NO_INCREMENT );
  30. return ntStatus;
  31. }

3. 创建内核线程

通过用户应用程序向设备驱动程序发送开启内核线程的IO请求,设备IO控制例程会调用StartThread函数处理该请求,在该函数中,用PsCreateSystemThread函数实现内核线程的创建。

  1. NTSTATUS StartThread(PSYSTHREAD_DEVICE_EXTENSION pdx)
  2. {
  3. NTSTATUS status;
  4. HANDLE hthread;
  5. //初始化event对象
  6. KeInitializeEvent(&pdx->evKill,
  7. NotificationEvent, // auto reset
  8. TRUE // initial state : FALSE ==> non-signaled
  9. );
  10. //创建ThreadProc
  11. status = PsCreateSystemThread(&hthread,
  12. THREAD_ALL_ACCESS,
  13. NULL,
  14. NULL,
  15. NULL,
  16. (PKSTART_ROUTINE) ThreadProc,
  17. pdx
  18. );
  19. if( !NT_SUCCESS(status))
  20. {
  21. DbgPrint(("Fail Start ThreadProc()!\n"));
  22. return status;
  23. }
  24. ObReferenceObjectByHandle(hthread,
  25. THREAD_ALL_ACCESS,
  26. NULL,
  27. KernelMode,
  28. (PVOID *) &pdx->thread,
  29. NULL
  30. );
  31. ZwClose(hthread);
  32. return STATUS_SUCCESS;
  33. }

4. 内核线程函数ThreadProc

该内核线程函数ThreadProc即为要实现定时程序的函数,以下是本文关注的在驱动中创建内核线程后,进行高精度定时的方法:

  1. #pragma code_seg("/SECTION:.my_data1,P")
  2. VOID ThreadProc(PSYSTHREAD_DEVICE_EXTENSION pdx)
  3. {
  4. int i, j;
  5. int count = 0;
  6. LARGE_INTEGER maxDur;
  7. LARGE_INTEGER s, e, f;
  8. LARGE_INTEGER last; // 上一次定时的Counter值
  9. LARGE_INTEGER dead_line; // 下一次定时Counter理论值
  10. LARGE_INTEGER timeout;
  11. KPRIORITY cur;
  12. KIRQL curIRQL, oldIRQL;
  13. NTSTATUS status;
  14. timeout.QuadPart = -1 * 10000000; // 1 second
  15. // 查询当前优先级
  16. cur = KeQueryPriorityThread(KeGetCurrentThread());
  17. DbgPrint("Thread's current priority: %d\n", (unsigned long)cur);
  18. // 设置优先级
  19. KeSetPriorityThread(KeGetCurrentThread(), 24);
  20. // 查询设置后的优先级
  21. cur = KeQueryPriorityThread(KeGetCurrentThread());
  22. DbgPrint("Have set thread's priority to: %d\n", (unsigned long)cur);
  23. //查询当前IRQL
  24. curIRQL = KeGetCurrentIrql();
  25. DbgPrint("Thread's current IRQL: %d\n", (unsigned long)curIRQL);
  26. //设置IRQL
  27. KeRaiseIrql(DISPATCH_LEVEL, &oldIRQL);
  28. // 查询设置后的IRQL
  29. curIRQL = KeGetCurrentIrql();
  30. DbgPrint("Have set thread's IRQL to: %d\n", (unsigned long)curIRQL);
  31. KeSetSystemAffinityThread(4);
  32. //定时程序开始
  33. DbgPrint("begin\n");
  34. s = KeQueryPerformanceCounter(&f);
  35. dead_line = s;
  36. last = s;
  37. maxDur.QuadPart = 3579;
  38. for (j=0; j<5; j++)
  39. {
  40. status = KeWaitForSingleObject(&pdx->evKill, Executive, KernelMode, FALSE, &timeout);
  41. if( status == STATUS_TIMEOUT )
  42. break;
  43. DbgPrint("==========================time: %d\n", j);
  44. DbgPrint("maxDur: %d\n", maxDur);
  45. for (i=0; i<545; i++)
  46. {
  47. dead_line.QuadPart += 3580;
  48. do
  49. {
  50. e = KeQueryPerformanceCounter(&f);
  51. } while (e.QuadPart < dead_line.QuadPart);
  52. if (e.QuadPart - last.QuadPart > maxDur.QuadPart)
  53. {
  54. count++;
  55. maxDur.QuadPart = e.QuadPart - last.QuadPart;
  56. DbgPrint("maxDur: %d\n", maxDur.QuadPart);
  57. }
  58. last = e;
  59. }
  60. for (i=545; i<1000; i++)
  61. {
  62. dead_line.QuadPart += 3579;
  63. do
  64. {
  65. e = KeQueryPerformanceCounter(&f);
  66. } while (e.QuadPart < dead_line.QuadPart);
  67. if (e.QuadPart - last.QuadPart > maxDur.QuadPart)
  68. {
  69. count++;
  70. maxDur.QuadPart = e.QuadPart - last.QuadPart;
  71. DbgPrint("max Dur: %d %d\n", maxDur.QuadPart);
  72. }
  73. last = e;
  74. }
  75. }
  76. KeLowerIrql(oldIRQL);
  77. DbgPrint("Thread function end");
  78. // 结束线程
  79. PsTerminateSystemThread(STATUS_SUCCESS);
  80. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注