1)驱动中使用到的线程是系统线程,在system进程中。创建线程API函数:PsCreateSystemThread;结束线程(线程内自行调用)API函数:PsTerminateSystemThread;关闭线程句柄API函数:ZwClose。以下代码忽略同步问题

  线程函数:

 void MyThreadProc(PVOID context)
 {
     PUNICODE_STRING str = (PUNICODE_STRING)context;
     //打印字符串
     KdPrint(("PrintInMyThread:%wZ\r\n",str));
     //结束自己
     PsTerminateSystemThread(STATUS_SUCCESS);
 }

  创建线程:

 UNICODE_STRING str = RTL_CONSTANT_STRING(L"hello!");
 HANDLE hThread = NULL;
 NTSTATUS status = PsCreateSystemThread(
     &hThread,//新线程句柄
     0L,NULL,NULL,NULL,
     MyThreadProc,//函数地址
     (PVOID)&str//传递参数
     );
 if ( !NT_SUCCESS(status) )
 {
     KdPrint(("CreateSystemThread failed!"));
 }

 ZwClose(hThread);

2)  在线程中睡眠API函数:KeDelayExecutionThread

//时间转换宏,这个是从毫秒转换到百纳秒(负数)
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)

void MySleep(LONG msec)
{
    LARGE_INTEGER my_interval;
    my_interval.QuadPart = DELAY_ONE_MILLISECOND;
    my_interval.QuadPart *= msec;
    KeDelayExecutionThread(KernelMode/*不允许报警*/,&my_interval);
}

  线程+睡眠函数可以实现定时器功能,但是定时器的回调函数运行在APC中断级别(较Passive中断级别高),有些函数就不能够被中断去执行;而睡眠线程运行在Passive级别,总是可以保证被执行。

3)Sleep函数对线程的延时是不准确的,它的执行原理是在每个轮转到自己的时间片时,判断被延时的时间是否达到,如果没有达到,则放弃当前时间片,这样的话,如果系统在做其它任务而迟迟不将时间片交付给运用程序,那么,延时函数将极度不准确。而对于定时器,是将定时器对象加入到系统队列统一减少“滴答”值来定时,当时间到则系统调用回调函数实现定时器中的工作内容,这是否会有严重延时暂时还不能确定!

4)使用同步事件:事件数据结构是KEVENT,它需要使用KeInitializeEvent函数来初始化,使用KeSetEvet来设置有信号状态,使用KeResetEvent来设置无信号状态,使用KeWaitForSingleObject来等待事件信号。事件不需要销毁。自动重设事件可用于线程互斥,一旦事件被设置为有信号,所有的等待线程中只有一个线程会进入临界区,其它的继续阻塞;而手动重设事件不能用于线程互斥,一旦事件被设置为有信号状态,所有等待该事件的线程都将得到释放,继续执行下面的代码,它可用于同步。

 #define DELAY_ONE_MICROSECOND (-10)
 #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)

 void MySleep(LONG msec)
 {
     LARGE_INTEGER my_interval;
     my_interval.QuadPart = DELAY_ONE_MILLISECOND;
     my_interval.QuadPart *= msec;
     KeDelayExecutionThread(KernelMode,,&my_interval);
 }

 void MyThreadProc(PVOID context)
 {
     PUNICODE_STRING str = (PUNICODE_STRING)context;
     //打印字符串
     int i;
      ; i <  ; i++)
     {
         MySleep();
         KdPrint(("PrintInMyThread:%wZ\r\n",str));
     }
     //设置有信号
     KeSetEvent(&event,
         IO_NO_INCREMENT,//预备给被唤醒线程临时提升优先级的增量
         FALSE//之后是否跟KeWaitForXXX等待(其它或者自身)事件
         );
     //结束自己
     PsTerminateSystemThread(STATUS_SUCCESS);
 }

  测试代码:

 //定义一个全局静态事件
 static KEVENT event;

 //在入口函数写
 UNICODE_STRING str = RTL_CONSTANT_STRING(L"hello!");
 HANDLE hThread = NULL;
 NTSTATUS status;
 //事件初始化
 KeInitializeEvent(&event,
     SynchronizationEvent,//自动复位
     FALSE//初始为无信号状态
     );
 //创建一个线程
 status = PsCreateSystemThread(
     &hThread,//新线程句柄
     0L,NULL,NULL,NULL,
     MyThreadProc,//函数地址
     (PVOID)&str//传递参数
     );
 if ( !NT_SUCCESS(status) )
 {
     KdPrint(("CreateSystemThread failed!"));
 }

 KdPrint(("Start KeWaitForSingleObject!"));

 KeWaitForSingleObject(&event,
     Executive,//等待原因,驱动程序设置为Executive
     KernelMode,//内核模式
     FALSE,//不允许警告
     NULL//等待超时时间:无限等待
     );

 ZwClose(hThread);

 KdPrint(("KeWaitForSingleObject return!"));

  这种等待线程的返回并不需要使用到线程同步,可用如下函数取代:

 //须包含Ntoskrnl.lib
 ZwWaitForSingleObject(hThread,
     FALSE,//不允许警告
     NULL//等待超时时间:无限等待
 );

  效果图:

《天书夜读:从汇编语言到windows内核编程》十 线程与事件的更多相关文章

  1. 《天书夜读:从汇编语言到windows内核编程》五 WDM驱动开发环境搭建

    (原书)所有内核空间共享,DriverEntery是内核程序入口,在内核程序被加载时,这个函数被调用,加载入的进程为system进程,xp下它的pid是4.内核程序的编写有一定的规则: 不能调用win ...

  2. 《天书夜读:从汇编语言到windows内核编程》八 文件操作与注册表操作

    1)Windows运用程序的文件与注册表操作进入R0层之后,都有对应的内核函数实现.在windows内核中,无论打开的是文件.注册表或者设备,都需要使用InitializeObjectAttribut ...

  3. 《天书夜读:从汇编语言到windows内核编程》六 驱动、设备、与请求

    1)跳入到基础篇的内核编程第7章,驱动入口函数DriverEnter的返回值决定驱动程序是否加载成功,当打算反汇编阅读驱动内核程序时,可寻找该位置. 2)DRIVER_OBJECT下的派遣函数(分发函 ...

  4. 《天书夜读:从汇编语言到windows内核编程》十一 用C++编写内核程序

    ---恢复内容开始--- 1) C++的"高级"特性,是它的优点也是它的缺点,微软对于使用C++写内核程序即不推崇也不排斥,使用C++写驱动需注意: a)New等操作符不能直接使用 ...

  5. 《天书夜读:从汇编语言到windows内核编程》四 windows内核调试环境搭建

    1) 基础篇是讲理论的,先跳过去,看不到代码运行的效果要去记代码是一个痛苦的事情.这里先跳入探索篇.其实今天的确也很痛苦,这作者对驱动开发的编译与调试环境介绍得太模糊了,我是各种尝试,对这个环境的搭建 ...

  6. 《天书夜读:从汇编语言到windows内核编程》九 时间与定时器

    1)使用如下自定义函数获取自系统启动后经历的毫秒数:KeQueryTimeIncrement.KeQueryTickCount void MyGetTickCount(PULONG msec) { L ...

  7. 《天书夜读:从汇编语言到windows内核编程》七 内核字符串与内存

    1)驱动中的字符串使用如下结构: typedef struct _UNICODE_STRING{ USHORT Length; //字符串的长度(字节数) USHORT MaximumLength; ...

  8. 《天书夜读:从汇编语言到windows内核编程》三 练习反汇编C语言程序

    1) Debug版本算法反汇编,现有如下3×3矩阵相乘的程序: #define SIZE 3 int MyFunction(int a[SIZE][SIZE],int b[SIZE][SIZE],in ...

  9. 《天书夜读:从汇编语言到windows内核编程》二 C语言的流程与处理

    1) Debug与Release的区别:前者称调试版,后者称发行版.调试版基本不优化,而发行版会经过编译器的极致优化,往往与优化前的高级语言执行流程会大相径庭,但是实现的功能是等价的. 2) 如下fo ...

随机推荐

  1. Java中Math.round()函数

    Math.round(11.5) = 12; Math.round(-11.5) = -11; Math.round()函数是求某个数的整数部分,且四舍五入.

  2. memcached讲解

    Memcached 最近空闲的是时候研究了一下缓存,根据公司环境对缓存进行了系统的了解,我们使用memcacheed进行缓存,下面结合我的 理解,以及网上的相关资料,memecached进行讲解. m ...

  3. php 连接mysql数据库以及增删改查

    php 连接数据库 一般是用面向对象的方法,需要先创建一个对象,即造一个连接对象,然后再写sql语句,(增改查删),最后执行sql语句 其中在创建连接对象时 我们用到的是MySQLI  是不区分大小写 ...

  4. Java集合源码分析(二)Linkedlist

    前言 前面一篇我们分析了ArrayList的源码,这一篇分享的是LinkedList.我们都知道它的底层是由链表实现的,所以我们要明白什么是链表? 一.LinkedList简介 1.1.LinkedL ...

  5. java基础---java语言概述

    一.计算机编程的两种范型 1.面向过程的模型---具有线性执行特点,认为是代码作用于数据. 2.面向对象的模型---围绕它的数据(即对象)和为这个数据定义的接口来组织程序:实际上是用数据控制代码的访问 ...

  6. 最美时光第三方UWP源码公开

    自己大概写了一个星期的成果.. 使用了官方最美时光app的UI和图片资源,并没有调用官方接口 https://files.cnblogs.com/files/loyieking/NiceCountDo ...

  7. 使用HTML DOM 来分配事件 —— onmouseover和onmouseout ,onmousedown和onmouseup

    一, onmouseover 和 onmouseout 事件 onmouseover 和 onmouseout 事件可用于在用户的鼠标移至 HTML 元素上方或移出元素时触发函数. 一个小例:鼠标未在 ...

  8. Android基础知识05—活动的生命周期

    ------ 活动的生命周期 ------ Android是使用任务Task来管理活动的,一个任务就是一组存放在栈里的活动的集合.每当启动一个活动 ,他就会在返回栈中入栈,并处于栈顶位置.而每当我们按 ...

  9. DNS over TLS到底有多牛?你想知道的都在这儿

    DNS over TLS,让电信.移动等各种ISP无法监视你的浏览轨迹...... SSL证书有助于客户端浏览器和网站服务器之间的加密连接. 这意味着在连接期间,所有的通信和活动都被遮蔽. 但通常意义 ...

  10. yii2之依赖注入与依赖注入容器

    一.为什么需要依赖注入 首先我们先不管什么是依赖注入,先来分析一下没有使用依赖注入会有什么样的结果.假设我们有一个gmail邮件服务类GMail,然后有另一个类User,User类需要使用发邮件的功能 ...