定时器
来源:互联网 发布:怎样学好历史高中知乎 编辑:程序博客网 时间:2024/05/17 02:46
转载自:http://mzf2008.blog.163.com/blog/static/355997862010112622923357/
1. I/O定时器
I/O定时器是DDK提供的一种定时器,使用这种定时器时,每间隔1S钟系统会调用一次I/O定时器例程。
I/O定时器可以为间隔N秒做定时,但是如果要实现毫秒级别间隔,微妙级别间隔,就需要用到DPC定时器。
初始化定时器:
NTSTATUS
IoInitializeTimer(
IN PDEVICE_OBJECT DeviceObject,
IN PIO_TIMER_ROUTINE TimerRoutine,
IN PVOID Context
);
开启I/O定时器:
VOID
IoStartTimer(
IN PDEVICE_OBJECT DeviceObject
);
停止I/O定时器:
VOID
IoStopTimer(
IN PDEVICE_OBJECT DeviceObject
);
示例代码:
用户层代码:
BOOL bRet = DeviceIoControl(hFile, TEST_TIME_START, NULL, 0, NULL, 0, &dwRet, NULL);
Sleep(10000);
bRet = DeviceIoControl(hFile, TEST_TIME_STOP, NULL, 0, NULL, 0, &dwRet, NULL);
定义设备扩展:
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名称
UNICODE_STRING ustrSymLinkName; //符号链接名
LONG ulTimeCount; //间隔时间
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
在DriverEntry中初始化I/O计时器:
#pragma INITCODE
extern "C" NTSTATUS DriverEntry (
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath )
{
。。。
IoInitializeTimer(pDevObj, TimeProc, NULL);
。。。
}
IRP_MJ_DEVICE_CONTROL派遣函数
#pragma PAGEDCODE
NTSTATUS HelloDDKDeviceControl(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("进入HelloDDKDeviceControl处理函数!\n"));
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulOut = stack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG ulIn = stack->Parameters.DeviceIoControl.InputBufferLength;
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
switch (code)
{
case TEST_TIME_START:
{
KdPrint(("开启I/O计数器!\n"));
pdx->ulTimeCount = 3;
//开启I/O定时器
IoStartTimer(pDevObj);
break;
}
case TEST_TIME_STOP:
{
KdPrint(("停止I/O计时器!\n"));
//停止I/O定时器
IoStopTimer(pDevObj);
break;
}
default:
status = STATUS_INVALID_VARIANT;
}
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
I/O定时器调用例程:
#pragma LOCKEDCODE //运行在DISPATCH_LEVEL的IRQL级别
VOID TimeProc(IN PDEVICE_OBJECT DeviceObject, IN PVOID Context)
{
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
KdPrint(("进入I/O计时器处理函数!\n"));
//递减计数
InterlockedDecrement(&pdx->ulTimeCount);
//当计数减到0之后,继续变为3,
LONG preCount = InterlockedCompareExchange(&pdx->ulTimeCount, 3, 0);
//每隔3秒,计数器一个循环,输出如下信息
if (preCount == 0)
{
KdPrint(("3秒钟过去了!\n"));
}
//证明该线程运行在任意线程上下文
PEPROCESS pEProcess = IoGetCurrentProcess();
PWSTR ProcessName = (PWSTR)((ULONG)pEProcess + 0x174);
KdPrint(("当前运行中的进程:%s", ProcessName));
}
2. DPC定时器
这种定时器更加灵活,可以对任意间隔时间进行定时。DPC定时器内部使用定时器对象KTIMER,当对定时器设定一个时间间隔后,每隔这段时间操作系统会将一个DPC例程插入DPC队列。当操作系统读取DPC队列时,对应的DPC例程会被执行。DPC定时器例程相当于定时器的回调函数。
在使用DPC定时器前,需要初始化DPC对象和定时器对象。
初始化定时器对象:
VOID
KeInitializeTimer(
IN PKTIMER Timer
);
初始化DPC对象:
VOID
KeInitializeDpc(
IN PRKDPC Dpc,
IN PKDEFERRED_ROUTINE DeferredRoutine,
IN PVOID DeferredContext
);
开启定时器:
BOOLEAN
KeSetTimer(
IN PKTIMER Timer,
IN LARGE_INTEGER DueTime,
IN PKDPC Dpc OPTIONAL
);
取消定时器:
BOOLEAN
KeCancelTimer(
IN PKTIMER Timer
);
注意;在调用KeSetTimer后,只会触发一次DPC例程。如果想周期的触发DPC例程,需要在DPC例程被触发后,再次调用KeSetTimer函数。
示例代码:
用户层:
ULONG ulMicroSeconds = 3000000;
BOOL bRet = DeviceIoControl(hFile, TEST_TIME_START, &ulMicroSeconds,
sizeof(ULONG), NULL, 0, &dwRet, NULL);
Sleep(10000);
bRet = DeviceIoControl(hFile, TEST_TIME_STOP, NULL, 0, NULL, 0, &dwRet, NULL);
定义设备扩展:
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName; //设备名称
UNICODE_STRING ustrSymLinkName; //符号链接名
KDPC dpc; //储存DPC对象
KTIMER timer; //储存定时器对象
LARGE_INTEGER pollTime; //记录定时器间隔时间
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
在DriverEntry中初始化:
KeInitializeTimer(&pDevExt->timer);
KeInitializeDpc(&pDevExt->dpc, DPCFunction, pDevObj);
IRP_MJ_DEVICE_CONTROL派遣函数:
#pragma PAGEDCODE
NTSTATUS HelloDDKDeviceControl(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)
{
NTSTATUS status = STATUS_SUCCESS;
KdPrint(("进入HelloDDKDeviceControl处理函数!\n"));
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulOut = stack->Parameters.DeviceIoControl.OutputBufferLength;
ULONG ulIn = stack->Parameters.DeviceIoControl.InputBufferLength;
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
switch (code)
{
case TEST_TIME_START:
{
KdPrint(("开启I/O计数器!\n"));
ULONG ulMicroSeconds = *(PULONG)pIrp->AssociatedIrp.SystemBuffer;
pdx->pollTime = RtlConvertLongToLargeInteger(-10 * ulMicroSeconds);
KeSetTimer(&pdx->timer, pdx->pollTime, &pdx->dpc);
break;
}
case TEST_TIME_STOP:
{
KdPrint(("停止I/O计时器!\n"));
KeCancelTimer(&pdx->timer);
break;
}
default:
status = STATUS_INVALID_VARIANT;
}
pIrp->IoStatus.Information = 0;
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
DPC派遣函数:
VOID DPCFunction(
IN struct _KDPC *Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
{
PDEVICE_OBJECT pDevObj = (PDEVICE_OBJECT)DeferredContext;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
KeSetTimer(&pdx->timer, pdx->pollTime, &pdx->dpc);
PEPROCESS pEProcess = IoGetCurrentProcess();
PWSTR ProcessName = (PWSTR)((ULONG)pEProcess + 0x174);
KdPrint(("ProcessName:%s", ProcessName));
}
注意:每次执行KeSetTimer只会触发一次DPC例程。为了能周期的调用DPC例程,应该在DPC例程中再次调用KeSetTimer,并且时间间隔保持不变。
3. 等待
(1) 使用KeWaitForSingleObject
首先初始化一个内核同步对象,其初始化状态为未激发。然后调用KeWaitForSingleObject,并对其设置timeout参数,该参数是需要等待的时间。
示例代码:
#pragma PAGEDCODE
VOID WaitMicroSecond(LONG MicroSeconds)
{
KdPrint(("进入延时函数!\n"));
KEVENT enent;
KeInitializeEvent(&enent, SynchronizationEvent, FALSE);
LARGE_INTEGER waitTime = RtlConvertLongToLargeInteger(-10 * MicroSeconds);
KeWaitForSingleObject(&enent, Executive,KernelMode, FALSE, &waitTime);
KdPrint(("离开延时函数!\n"));
}
(2) 使用KeDelayExecutionThread
该内核函数和KeWaitForSingleObject类似,都是强制当前线程进入睡眠状态。经过指定的睡眠时间后,线程恢复运行。
示例代码:
#pragma PAGEDCODE
VOID WaitMicroSecond(LONG MicroSeconds)
{
KdPrint(("进入延时函数!\n"));
LARGE_INTEGER waitTime = RtlConvertLongToLargeInteger(-10 * MicroSeconds);
KeDelayExecutionThread(Executive, KernelMode, &waitTime);
KdPrint(("离开延时函数!\n"));
}
(3) 使用KeStallExecutionProcessor
该内核函数是让CPU处于忙等待状态,而不是处于睡眠,类似于自旋锁。经过指定时间后,继续让线程运行。
因为这种方法浪费宝贵的CPU时间,因此DDK文档规定,此函数不宜超过50微妙。由于没有将线程进入睡眠状态,也不会发生进程间的切换,因此这种方法的延时比较精确。
示例代码:
#pragma PAGEDCODE
VOID WaitMicroSecond(ULONG MicroSeconds)
{
KdPrint(("延时 %d 微妙!\n", MicroSeconds));
KeStallExecutionProcessor(MicroSeconds);
KdPrint(("离开延时函数,线程继续执行!\n"));
}
(4) 使用定时器
这里用到的定时器对象和前面讲的DPC定时器略有不同,这里没有用到DPC对象和DPC例程,因此当指定时间到后,不会进入DPC例程。
定时器对象和其他内核同步对象一样,也是有两个状态,一个是未激发状态,一个是激发状态。当初始化定时器的时候,定时器处于未激发状态。当使用KeSetTimer后,经过指定时间后,进入激发状态。这样就可以是用KeWaitForSingleObject函数对定时器对象进行等待。
示例代码:
#pragma PAGEDCODE
VOID WaitMicroSecond(ULONG MicroSeconds)
{
KdPrint(("延时 %d 微妙!\n", MicroSeconds));
KTIMER timer;
KeInitializeTimer(&timer);
LARGE_INTEGER waitTime = RtlConvertLongToLargeInteger(-10 * MicroSeconds);
KeSetTimer(&timer, waitTime, NULL);
KeWaitForSingleObject(&timer, Executive, KernelMode, FALSE, NULL);
KdPrint(("离开延时函数,线程继续执行!\n"));
}
4. 与时间相关的其他内核函数
获取当前系统时间的内核函数:
VOID
KeQuerySystemTime(
OUT PLARGE_INTEGER CurrentTime
);
将系统时间转换为当前时区对应的时间:
VOID
ExSystemTimeToLocalTime(
IN PLARGE_INTEGER SystemTime,
OUT PLARGE_INTEGER LocalTime
);
将当前时区的时间转换为系统时间:
VOID
ExLocalTimeToSystemTime(
IN PLARGE_INTEGER LocalTime,
OUT PLARGE_INTEGER SystemTime
);
由当前的年月日得到系统时间:
BOOLEAN
RtlTimeFieldsToTime(
IN PTIME_FIELDS TimeFields,
IN PLARGE_INTEGER Time
);
typedef struct TIME_FIELDS {
CSHORT Year;
CSHORT Month;
CSHORT Day;
CSHORT Hour;
CSHORT Minute;
CSHORT Second;
CSHORT Milliseconds;
CSHORT Weekday;
} TIME_FIELDS;
由系统时间得到当前的年月日等信息:
VOID
RtlTimeToTimeFields(
IN PLARGE_INTEGER Time,
IN PTIME_FIELDS TimeFields
);
示例代码:
#pragma PAGEDCODE
VOID TimeTest()
{
LARGE_INTEGER systemTime;
KeQuerySystemTime(&systemTime);
TIME_FIELDS timeFields;
RtlTimeToTimeFields(&systemTime, &timeFields);
KdPrint(("Current year %d", timeFields.Year));
KdPrint(("Current month %d", timeFields.Month));
KdPrint(("Current day %d", timeFields.Day));
KdPrint(("Current hour %d", timeFields.Hour));
KdPrint(("Current minute %d", timeFields.Minute));
KdPrint(("Current second %d", timeFields.Second));
KdPrint(("Current millisecond %d", timeFields.Milliseconds));
KdPrint(("Current weekday %d", timeFields.Weekday));
}
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 定时器
- 需要“jquery”ScriptResourceMapping,请添加一个名为 jquery的 ScriptResourceMapping解决办法
- 线程同步(2) - 内核模式下的线程同步
- 操作系统基础知识--进程与进程同步
- C++11之智能指针(上)
- IRP的同步
- 定时器
- paip.提升性能---string split
- VC中EditCtrl的自动换行
- 使用清华源跑pip
- virtualbox 中实现xp与ubuntu文件共享
- Codeforces Round #212 (Div. 2)
- fltk配置到VS2008
- Django URL
- Ubuntu修改计算机名