zoukankan      html  css  js  c++  java
  • 《Windows内核编程》---系统线程和同步事件

    系统线程:

    在驱动中生成的线程一般是系统线程,系统线程所在的进程名为“System”,用到的内核API函数是:

    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;接着三个可选参数都赋值为0StartRoutine参数是用于该线程启动时执行的函数;StartContext是线程函数的参数。线程函数的原型如下:

    VOID CustomThreadProc(IN PVOID context); //context自然就是由上面的StartContext传入的

    注意:线程在结束时应该自己调用PsTerminateSystemThread函数来完成,此外得到的线程句柄ThreadHandle要使用ZwClose来关闭。(关闭句柄并不等于结束线程)

    下面的函数演示怎样创建并使用系统线程:

    //线程函数

    VOID ASCEThreadProc(PVOID context)

    {

             PUNICODE_STRING str = (PUNICODE_STRING)context;

             //打印字符串

             KdPrint((Print In ASCEThead:%wZ/r/n, str));

             //结束自己

             PsTerminateSystemThread(STATUS_SUCCESS);

    }

     

    VOID ASCEFunction()

    {

             UNICODE_STRING str = RTL_CONSTANT_STRING(LASCE!);

             HANDLE thread = NULL;

             NTSTATUS status;

             status = PsCreateSystemThread(

    &thread, 0L, NULL, NULL, NULL, ASCEThreadProc, (PVOID)&str);

             if(!NT_SUCCESS(status))

             {

                       //错误处理

                       -.

    }

    //成功了,则做自己的事

    -..

    //最后关闭句柄

    ZwClose(thread);

    }

    上面代码存在如下错误:ASCEThreadProc执行时,ASCEFunction可能已经执行完了。而执行完之后,堆栈中的str就无效了。此时再执行KdPrint打印str一定会蓝屏。

    合理的方法是在堆中分配str的空间,或者str必须在全局空间中。当然,我们也可以在PsCreateSystemThread结束之后,在它的后面加上一个等待线程结束的语句。

     

    在线程中睡眠:

    在驱动编程中使用到的用于睡眠的内核函数是:

    NTSTATUE KeDelayExecutionThread(

    IN KPROCESSOR_MODE WaitMode,

    IN BOOLEAN Alertable,

    IN PLARGE_INTEGER Interval

    );

    其中参数WaitMode应该总是赋值为KernelMode,因为我们现在是在内核编程;Alertable表示是否允许线程报警(用于重新唤醒);Interval表示要睡眠多长时间。

    下面是简单的睡眠函数,它可以指定睡眠多少毫秒:

    #define DELAY_ONE_MICROSECOND (-10)

    #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)

    VOID ASCESleep(LONG msec)

    {

             LARGE_INTEGER asceInterval;

             asceInterval.QuadPart = DELAY_ONE_MILLISECOND;

             asceInterval.QuadPart *= msec;

             KeDelayExecutionThread(KernelMode, 0, &asceInterval);

    }

     

    同步对象:

    内核中的事件是一个数据结构,这个结构的指针可以当作一个参数传入一个等待函数中。如果这个事件不被“设置”,则这个等待函数不会返回,这个线程被阻塞;如果这个事件被“设置”,则等待结束,线程可以继续执行。

    如果一个线程需要等待另一个线程完成某事之后才能做某事,则可以使用事件等待。这个数据结构就是KEVENT,这个结构总是用KeInitializeEvent初始化:

    VOID KeInitializeEvent(

    IN PRKEVENT Event,

    IN EVENT_TYPE Type,

    IN BOOLEAN State

    );

    参数Event是要初始化的事件;Type是事件类型;参数State是初始化状态,一般设置为FALSE,也就是未设置状态,这样等待线程需要等待。注意,事件不需要销毁。

    设置事件使用函数KeSetEvent

    LONG KeSetEvent(

    IN PRKEVENT Event,       //要设置的事件

    IN KPRIORITY Increment,      //用于提升优先权,可设为0

    IN BOOLEAN Wait//表示函数后面是否紧接着一个KeWaitSingleObject来等待这个事件,

    //一般设置为TRUE(事件初始化后,一般就要开始等待了)

    );

     

    使用事件的简单代码如下:

    //等待一个事件

    KEVENT event;

    //事件初始化

    KeInitializeEvent(&event, SynchronizationEvent, TRUE);

    --

    //事件初始化之后就可以使用了,在一个函数中,我们可以等待某个事件

    //如果这个事件没有被人设置,那就会阻塞在这里继续等待

    KeWaitForSingleObject(&event, Executive, KernelMode, 0, 0);

    -..

     

    //在另一个函数或其他地方,设置了这个事件。而一旦设置了这个事件

    //前面等待的地方就会开始继续执行

    KeSetEvent(&event);

    上面代码中KeInitializeEvent中使用了SynchronizationEvent,导致这个事件成为所谓的“自动重设”事件。一个事件如果被设置,那么所有KeWaitForSingleObject等待这个事件的地方都会通过。如果要继续重复使用这个事件,必须重设这个事件。当KeInitializeEvent第二个参数设置为NotificationEvent时,这个事件必须要手动重设才能使用。手动重设使用函数:

    LONG KeResetEvent(

    IN PRKEVENT Event

    );

    上面代码中的事件初始化为SynchronizationEvent事件,因此只有一个线程的KeWaitForSingleObject可以通过,通过之后被自动重设,其他的线程只能继续等待,这就是一个同步事件

    不能起到同步作用的是通知事件(NotificationEvent),注意,不能用手工设置通知事件的方式来取代同步事件,Dont be stupid, ok?

     

    下面使用同步事件来改进上面系统线程中的例子,即在线程中打印结束之后,设置同步事件,外面的函数再返回:

    static KEVENT asceEvent;

    //线程函数

    VOID ASCEThreadProc(PVOID context)

    {

             PUNICODE_STRING str = (PUNICODE_STRING)context;

             KdPrint(("Print in ASCEThread: %wZ/r/n", str));

             KeSetEvent(&asceEvent);     //设置事件

             PsTerminateSystemThread(STATUS_SUCCESS);

    }

     

    //生成线程的函数

    VOID ASCEFunction()

    {

             UNICODE_STRING str = RTL_CONSTANT_STRING(L"SEE YOU IN ANOTHER LIFE BROTHER");

             HANDLE thread = NULL;

             NTSTATUS status;

             KeInitializeEvent(&asceEvent, SynchronizationEvent, TRUE);       //初始化事件

             status = PsCreateSystemThread(

                       &thread, 0L, NULL, NULL, NULL, ASCEThreadProc, (PVOID)&str);

             if(!NT_SUCCESS(status))

             {

                       //错误处理

             }

             ZwClose(thread);

             //等待事件结束在返回

             KeWaitForSingleObject(&asceEvent, Executive, KernelMode, 0, 0);

    }

    注意,等待线程结束不一定要用事件,线程本身也可以用来等待。上面函数的缺点是不能起到并发执行的作用。

    jpg 改 rar 

  • 相关阅读:
    项目乱码导致原因
    springmvc配置文件
    IntelliJ IDEA启动Tomcat后,却无法访问Tomcat主页 等一系列问题
    java错题
    java 从键盘录入的三种方法
    java内部类 和外部类的区别
    多线程
    24中模式详解
    java的强制类型转换
    正则表达式
  • 原文地址:https://www.cnblogs.com/kuangke/p/9397745.html
Copyright © 2011-2022 走看看