内核定时器
在Win32上面基本上定时做事情事情的话, 一般会使用WM_TIMER消息, 当然其他还有很多种选择, 同样的, 在内核中一样很多种的定时器对象可供使用, 使用起来也是那么的简单.
那么来看看一般都使用哪几种, 首先就是IO/定时器了, 这种定时器固定了是1s/次, 当然能够间隔一秒的话, 其实间隔多秒也是可以的在定时器例程中记录一个计时器, 这样就可以达到间隔多次的目的, 不过最精确就是1S了, Win32下面的WM_TIMER好像是55ms一次. 看来精确度还是不够啊, 没事下面还有其他的. 这边先看看这个间隔1S的, 用起来也不过是几个函数的调用而已, 重要的是记住这个IO/定时器运行在Dispathc_Level级别上, 这个级别是有限制的. 要记住这点.另外定时器的运行线程是不固定的, 哪里都有可能, 所以不能够使用低2G的内存地址. 这个很好明白了!
第2种就是DPC定时器了, 这个定时器的精度就高多了, DPC定时器的Timer对象初始化函数很容易和上面那个搞混, 一个是IoInitializeTimer一个是KeInitializeTimer我倒, 这个Dpc定时器是只有一次的, 所以如果需要循环的做某些事情, 那么在Dpc例程后面还要调用KeSetTimer重新计时. 当然这个Dpc定时器计时的办法也是很掉链子, 内部用的是100us单位, 和我们一般使用的ms都不一样. 伤不起啊! 这边有个计算方法,1000 * 1000 * 2 * -10 这个就是间隔2秒了, 直接套用就成, 倒.DPC例程也是运行在Dispathc_Level注意了. 分页内存不能够搞!
上面基本上说的是想基于定时器来循环做一些事情, 显然我们还有需求就是等待了. 一般都使用KeWaitForSingleObject, 下面的代码也有体现.然后第2中方法呢使用KeDelayExecutionThread, 这号函数也是强制当前线程进入睡眠, 经过指定的睡眠时间后, 线程恢复运行!这号函数非常好用.第3中方法就是摆摆样子了, KeStallExecutionProcesor这个函数类似于自旋锁, 该内核函数是让CPU处于等待状态, 不是睡眠. 经过指定的时间后, 继续让线程运行, DDK说不要超过50us, 我倒, 这个基本就没有什么用处了.
第4种的话, 其实还是前面的Dpc定时器那种, 不过这边不用Dpc对象和Dpc例程, 因此当指定时间到了以后不进入Dpc例程, 就相当于睡眠了.定时器对象和其他内核同步对象一样, 有两种状态, 一种是激发状态, 一种是未激发状态, 所以也可以用KeWaitForSingleObject等待. 差不多就是这些东西了!
/* 内核下的定时器函数, 和一系列其他函数的使用 0环代码 TAB = 8 */ #include <ntddk.h> #define TIMER_OUT 10 typedef struct tagDevice_Ext { ULONG ulTimeOut; PKEVENT phEvent; //事件对象指针 KTIMER StTimer; //Dpc计时器对象 KDPC StDpc; //Dpc对象 LARGE_INTEGER liInterger; //Dpc间隔时间 } DEVICE_EXT, *PDEVICE_EXT; //=========================================================================== //内核线程函数1 //=========================================================================== #pragma code_seg( "PAGE" ) VOID SysRoutine1( PVOID pContext ) { PEPROCESS pEprocess = NULL; PTSTR pStrProcessName = NULL; PAGED_CODE(); //获取进程名称 pEprocess = IoGetCurrentProcess(); pStrProcessName = ( PTSTR )( ( ULONG )pEprocess + 0x174 ); KdPrint( ( "进程名称:%s\n", pStrProcessName ) ); //停止50毫秒,我倒, 我改成5000都没有效果 KeStallExecutionProcessor( 50 ); PsTerminateSystemThread( STATUS_SUCCESS ); } //=========================================================================== //打印进程和时间信息 //=========================================================================== VOID PrintInfo() { TIME_FIELDS StTime; PEPROCESS pEprocess = NULL; PTSTR pStrProcessName = NULL; LARGE_INTEGER liSysTime; LARGE_INTEGER liLocalTime; //获取进程名称 pEprocess = IoGetCurrentProcess(); pStrProcessName = ( PTSTR )( ( ULONG )pEprocess + 0x174 ); KdPrint( ( "进程名称:%s\n", pStrProcessName ) ); //得到当前系统时间 KeQuerySystemTime( &liSysTime ); //从系统时间转换成当地时区时间 ExSystemTimeToLocalTime( &liSysTime, &liLocalTime ); //由当地时区时间得到月日年信息 RtlTimeToTimeFields( &liLocalTime, &StTime ); //显示年月日等信息 KdPrint( ( "年%d/", StTime.Year ) ); KdPrint( ( "月%d/", StTime.Month ) ); KdPrint( ( "日%d/", StTime.Day ) ); KdPrint( ( "星期%d\n", StTime.Weekday ) ); KdPrint( ( "%d:", StTime.Hour ) ); KdPrint( ( "%d:", StTime.Minute ) ); KdPrint( ( "%d:", StTime.Second ) ); KdPrint( ( "%d\n\n", StTime.Milliseconds ) ); } //=========================================================================== //定时器过程 //=========================================================================== #pragma code_seg() VOID TimerRoutine( PDEVICE_OBJECT pDeviceObj, PVOID pContext ) { PDEVICE_EXT pDeviceExt = NULL; PAGED_CODE_LOCKED(); PrintInfo(); //--------------------------------------------------------------------------- pDeviceExt = pDeviceObj->DeviceExtension; ASSERT( pDeviceExt != NULL ); if ( pDeviceExt->ulTimeOut == 0 ) { KeSetEvent( pDeviceExt->phEvent, IO_NO_INCREMENT, FALSE ); } else { //原子-- InterlockedDecrement( &pDeviceExt->ulTimeOut ); } } //=========================================================================== //Dpc例程(Dpc例程只能搞一次, 所以每次在后面还要设置一次) //=========================================================================== #pragma code_seg( ) VOID DpcRoutine( PKDPC pDpc, PVOID pContext, PVOID SysArg1, PVOID SysArg2 ) { PDEVICE_EXT pDeviceExt = NULL; PAGED_CODE_LOCKED(); PrintInfo(); pDeviceExt = ( ( PDEVICE_OBJECT )pContext )->DeviceExtension; ASSERT( pDeviceExt != NULL ); if ( pDeviceExt->ulTimeOut == 0 ) { KeSetEvent( pDeviceExt->phEvent, IO_NO_INCREMENT, FALSE ); } else { //开启Dpc例程 KeSetTimer( &pDeviceExt->StTimer, pDeviceExt->liInterger, &pDeviceExt->StDpc ); //原子-- InterlockedDecrement( &pDeviceExt->ulTimeOut ); } } //=========================================================================== //驱动入口 //=========================================================================== #pragma code_seg( "INIT" ) NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) { ULONG* pUlTime = NULL; HANDLE hThread; NTSTATUS Status; KEVENT hEvent; PETHREAD pThread = NULL; PDEVICE_OBJECT pDevice = NULL; PDEVICE_EXT pDeviceExt = NULL; //--------------------------------------------------------------------------- //创建一个线程然后等待, 知道线程对象返回 //--------------------------------------------------------------------------- //创建线程(参数4指定是用户线程函数系统线程, 很明显这里是系统线程) Status = PsCreateSystemThread( &hThread, 0, NULL, NULL, NULL, &SysRoutine1, NULL ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "创建线程失败!\n" ) ); return Status; } //将线程句柄转化为指针 Status = ObReferenceObjectByHandle( hThread, 0, *PsThreadType, KernelMode, ( PVOID* )&pThread, NULL ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "线程句柄转换指针失败!\n" ) ); return Status; } //等待线程对象(创建线程返回的是句柄, 所以需要转成指针) KeWaitForSingleObject( pThread, Executive, KernelMode, FALSE, NULL ); //这号函数也是可以等待的, 句柄. //ZwWaitForSingleObject( hThread, TRUE, 0 ); //--------------------------------------------------------------------------- //创建一个定时器1s一次 //--------------------------------------------------------------------------- KeInitializeEvent( &hEvent, NotificationEvent, FALSE ); Status = IoCreateDevice( pDriverObj, sizeof( DEVICE_EXT ), NULL, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevice ); if ( !NT_SUCCESS( Status ) ) { KdPrint( ( "创建设备失败!\n" ) ); return Status; } ASSERT( pDevice != NULL ); pDeviceExt = pDevice->DeviceExtension; pDeviceExt->phEvent = &hEvent; pDeviceExt->ulTimeOut = TIMER_OUT; //初始化定时器对象和启动定时器 IoInitializeTimer( pDevice, &TimerRoutine, NULL ); IoStartTimer( pDevice ); //等待事件对象置位 KeWaitForSingleObject( &hEvent, Executive, KernelMode, FALSE, NULL ); //停止定时器 IoStopTimer( pDevice ); //--------------------------------------------------------------------------- //Dpc例程试验, //--------------------------------------------------------------------------- pDeviceExt->ulTimeOut = TIMER_OUT; //负数表示间隔多少时间, 单位是ns 1000ns=1ms, 1000 ms = 1s pDeviceExt->liInterger = RtlConvertLongToLargeInteger( 1000 * 1000 * 2 * -10 ); //初始化计时器对象 KeInitializeTimer( &pDeviceExt->StTimer ); //初始化DPC对象 KeInitializeDpc( &pDeviceExt->StDpc, &DpcRoutine, pDevice ); //开启Dpc例程 KeSetTimer( &pDeviceExt->StTimer, pDeviceExt->liInterger, &pDeviceExt->StDpc ); KeResetEvent( &hEvent ); //等待事件对象置位 KeWaitForSingleObject( &hEvent, Executive, KernelMode, FALSE, NULL ); //取消定时器和事件对象 KeCancelTimer( &pDeviceExt->StTimer ); KeClearEvent( &hEvent ); //--------------------------------------------------------------------------- if ( pDevice ) { IoDeleteDevice( pDevice ); } KdPrint( ( "DriverEntry调用完毕!\n" ) ); return STATUS_UNSUCCESSFUL; }
发表评论
Warning: Undefined variable $user_ID in /www/wwwroot/joenchen.com/wp-content/themes/x-agan/comments.php on line 66
您必须登录 才能进行评论。