内核定时器

在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;
}

网友评论:

  1. minecraft 说:

    For hottest news you have to pay a quick visit internet and on internet I
    found this website as a best web page for hottest updates.

  2. minecraft 说:

    Hello colleagues, how is all, and what you desire to say concerning this post, in my view its in fact remarkable
    for me.

  3. minecraft free download 2018 说:

    Stunning quest there. What happened after? Take care!

  4. tinder dating site 说:

    I am really delighted to glance at this blog posts which consists of plenty of valuable facts, thanks for providing such information.

发表评论

发表评论前,请选对水果: 葡萄