文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>9、Windows驱动开发技术详解笔记(5) 基本语法回顾

9、Windows驱动开发技术详解笔记(5) 基本语法回顾

时间:2010-10-18  来源:edwardlewiswe

5、在驱动中获取系统时间

1)获取启动毫秒数

在ring3 我们可以通过一个GetTickCount 函数来获得自系统启动开始的毫秒数,在ring0也有一个与之对应的KeQueryTickCount 函数。不幸的是,这个函数并不能直接返回毫秒数,它返回的是“滴答”数,而一个时钟“滴答”到底是多久,这在不同的系统中可能是不同的,因此我们还需要另外一个函数的辅助,即KeQueryTimeIncrement 函数。KeQueryTimeIncrement 函数可以返回一个“滴答”表示多少个100 纳秒,注意这里的单位是100 纳秒。

2)获取系统时间

在ring3 获取系统时间是非常简单的,我们直接使用GetLocalTime 就可以通过一个系统时间结构体SYSTEMTIME 来返回当前时间。到了ring0我们可以使用KeQuerySystemTime来获得当前时间,但它其实是一个格林威治时间,与ring3得到的LocalTime 不同,因此我们还需要使用ExSystemTimeToLocalTime

函数将这个格林威治时间转换成当地时间。事情到这里还没有结束,现在我们获得的当地时间不是一个容易阅读的格式,因此我们还要使用RltTimeToTimeFieldh 函数将其转换成容易阅读的格式。

3)两个小例程

/************************************************************************

* 函数名称:MyGetTickCount

* 功能描述:获取tick数目

* 参数列表:

* 返回 值:返回状态

*************************************************************************/

VOID

MyGetTickCount()

{

LARGE_INTEGER tick_count;

ULONG inc;

inc = KeQueryTimeIncrement();

KeQueryTickCount(&tick_count);

// 因为1 毫秒等于1000000 纳秒,而inc 的单位是100 纳秒

// 所以除以10000 即得到当前毫秒数

tick_count.QuadPart *= inc;

tick_count.QuadPart /= 10000;

KdPrint(("[Test] TickCount : %d", tick_count.QuadPart));

}

/************************************************************************

* 函数名称:MyGetCurrentTime

* 功能描述:获取当前系统时间

* 参数列表:

* 返回 值:返回状态

*************************************************************************/

VOID

MyGetCurrentTime()

{

LARGE_INTEGER CurrentTime;

LARGE_INTEGER LocalTime;

TIME_FIELDS TimeFiled;

static WCHAR Time_String[32] = {0};

// 这里得到的其实是格林威治时间

KeQuerySystemTime(&CurrentTime);

// 转换成本地时间

ExSystemTimeToLocalTime(&CurrentTime, &LocalTime);

// 把时间转换为容易理解的形式

RtlTimeToTimeFields(&LocalTime, &TimeFiled);

KdPrint(("[Test] NowTime : %4d-%2d-%2d %2d:%2d:%2d",

TimeFiled.Year, TimeFiled.Month, TimeFiled.Day,

TimeFiled.Hour, TimeFiled.Minute, TimeFiled.Second));

}

6、在驱动中创建内核线程

1)创建

在ring3 我们可以使用CreateThread这个Win32 API 创建线程,在ring0也有与之对应的内核函数PsCreateSystemThread。

这个函数与CreateThread的使用很相似,它可以通过第一个参数返回线程的句柄,最后两个参数分别指定线程函数的地址和参数,在ring3我们就是这么做的。

我们使用CreateThread创建的线程只属于当前进程(不过CreateRemoteThread函数可以在指定进程中创建线程),而PsCreateSystemThread 函数默认情况下创建的却是一个系统进程,它属于进程名为“system”,PID=4的这个进程。不过PsCreateSystemThread也是可以创建用户线程的,这取决于它的第四个参数ProcessHandle,如果它为空,则创建的即系统线程;如果它是一个进程句柄,则创建的就是属于该指定进程的用户线程。

线程函数是一个非常重要的部分,它决定了该线程具有什么样的功能。线程函数必须按照如下规范声明:

VOID ThreadProc(IN PVOID context);

这个VOID指针参数通过强制转换可以达到很多特殊效果,给予了我们很大的自由度。我们还需要注意的一点,在内核里创建的线程必须自己调用PsTerminateSystemThread 来结束自身,它不能像ring3 的线程那样可以在执行完毕后自动结束。

NTSTATUS

PsCreateSystemThread(

OUT PHANDLE ThreadHandle,

IN ULONG DesiredAccess,

IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,

IN HANDLE ProcessHandle OPTIONAL,

OUT PCLIENT_ID ClientId OPTIONAL,

IN PKSTART_ROUTINE StartRoutine,

IN PVOID StartContext);

这个函数的参数也很多。经验如下:ThreadHandle用来返回句柄。放入一个句柄指针即可。DesiredAccess总是填写0。后面三个参数都填写NULL。最后的两个参数一个用于改线程启动的时候执行的函数。一个用于传入该函数的参数。

2)线程同步

虽然多线程并不是真正的并发运行,但由于CPU分配的时间片很短,看起来它们就像是并发运行的一样。

此前我们曾经介绍过自旋锁,它就是一种典型的同步方案,不过在线程同步的时候通常不使用它,而是使用事件通知,此外还有类似ring3的临界区、信号灯等方法。

下面我们介绍使用KEVENT事件对象进行同步的方法。

在使用KEVENT 事件对象前,需要首先调用内核函数KeInitialize Event 对其初始化,这

个函数的原型如下所示:

VOID

KeInitializeEvent(

IN PRKEVENT Event,

IN EVENT_TYPE Type,

IN BOOLEAN State);

第一个参数Event 是初始化事件对象的指针;第二个参数Type表明事件的类型。事件分两种类型:一类是“通知事件”,对应参数为NotificationEvent,另一类是“同步事件”,对应参数为SynchronizationEvent;第三个参数State 如果为TRUE,则事件对象的初始化状态为激发状态,否则为未激发状态。

如果创建的事件对象是“通知事件”,当事件对象变为激发态时,需要我们手动将其改回未激发态。如果创建的事件对象是“同步事件”,当事件对象为激发态时,如果遇到相应的KeWaitForXXXX 等内核函数,事件对象会自动变回到未激发态。设置事件的函数是KeSetEvent,可通过该函数修改事件对象的状态。

参考

【1】Windows 驱动开发技术详解

【2】http://msdn.microsoft.com/en-us/library/ff565757%28VS.85%29.aspx

【3】Windows驱动学习笔记,灰狐

相关阅读 更多 +
排行榜 更多 +
方块枪战战场安卓版

方块枪战战场安卓版

飞行射击 下载
战斗火力射击安卓版

战斗火力射击安卓版

飞行射击 下载
空中防御战安卓版

空中防御战安卓版

飞行射击 下载