Windows驱动
+ -

Windows内核驱动创建线程

2021-07-01 1089 0

有时候需要使用线程来完成一个或者一组任务。这些任务可能耗时过长,而开发者又不想让当前系统停止下来等待。在驱动中停止等待很容易使整个系统陷入“停顿”,最后可能只能重启电脑。但一个单独的线程长期等待,还不至于对系统造成致命的影响。另一些任务是希望长期、不断的执行,比如不断写入日志。为此启动一个特殊的线程来执行它们是最好的方法。

在驱动中生成的线程一般是系统线程。系统线程所在的进程名为“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。后面三个参数都填写NULL。最后的两个参数一个用于改线程启动的时候执行的函数。一个用于传入该函数的参数。

下面要关心的就是那个启动函数的原型。这个原型比起定时器回调函数倒是异常的简单,没有任何多余的东西:

    VOID CustomThreadProc(IN PVOID context)

可以传入一个参数,就是那个context。context就是PsCreateSystemThread中的StartContext。值得注意的是,线程的结束应该在线程中自己调用PsTerminateSystemThread来完成。此外得到的句柄也必须要用ZwClose来关闭。但是请注意:关闭句柄并不结束线程。

下面举一个例子。这个例子传递一个字符串指针到一个线程中打印一下。然后结束该线程。当然打印字符串这种事情没有必要单独开一个线程来做。这里只是一个简单的示例。请注意,这个代码中有一个隐藏的错误,请读者指出这个错误是什么:

// 我的线程函数。传入一个参数,这个参数是一个字符串。
VOID MyThreadProc(PVOID context)
{
    PUNICODE_STRING str = (PUNICODE_STRING)context;
    // 打印字符串
    KdPrint((“PrintInMyThread:%wZ\r\n”,str));
    // 结束自己。
    PsTerminateSystemThread(STATUS_SUCCESS);
} 

VOID MyFunction()
{
    UNICODE_STRING str = RTL_CONSTANT_STRING(L“Hello!”);
    HANDLE thread = NULL;
    NTSTATUS status;

    status = PsCreateSystemThread(
        &thread,0L,NULL,NULL,NULL,MyThreadProc,(PVOID)&str);
    if(!NT_SUCCESS(status))
    {
        // 错误处理。
        …
    }
    // 如果成功了,可以继续做自己的事。之后得到的句柄要关闭
    ZwClose(thread);
}

以上错误之处在于:MyThreadProc执行的时候,MyFunction可能已经执行完毕了。执行完毕之后,堆栈中的str已经无效。此时再执行KdPrint去打印str一定会蓝屏。这也是一个非常隐蔽,但是非常容易犯下的错误。

合理的方法是是在堆中分配str的空间。或者str必须在全局空间中。请读者自己写出正确的方法。

但是读者会发现,以上的写法在正确的代码中也是常见的。原因是这样做的时候,在PsCreateSystemThread结束之后,开发者会在后面加上一个等待线程结束的语句。

这样就没有任何问题了,因为在这个线程结束之前,这个函数都不会执行完毕,所以栈内存空间不会失效。

这样做的目的一般不是为了让任务并发。而是为了利用线程上下文环境而做的特殊处理。比如防止重入等等。在后面的章节读者会学到这方面的技巧。

0 篇笔记 写笔记

Windows内核线程睡眠
许多读者一定使用过Sleep函数。这能使程序停下一段时间。许多需要连续、长期执行,但是又不希望占太多CPU使用率的任务,可以在中间加入睡眠。这样能使CPU使用率大大降低。即使睡眠的时间非常短(几十个毫秒)。在驱动中也可以睡眠。使用到的内核函数的原型如下: NTSTATUS Ke......
Windows内核驱动创建线程
有时候需要使用线程来完成一个或者一组任务。这些任务可能耗时过长,而开发者又不想让当前系统停止下来等待。在驱动中停止等待很容易使整个系统陷入“停顿”,最后可能只能重启电脑。但一个单独的线程长期等待,还不至于对系统造成致命的影响。另一些任务是希望长期、不断的执行,比如不断写入日志。为此启动一个特殊的线程......
Windows驱动线程创建与退出
在应用层时,我们可以使用CreateThread来创建一个线程,这时如果创建成功,会退回一个线程句柄。而当创建的这个线程退出时,这个线程变为激活态,即我们可以通过WatiForSingleObject返回WAIT_OBJECT_0.如:HANDLE hThread = CreateThread(......
CreateThreadpoolWork线程
PTP_WORK WINAPI CreateThreadpoolWork( __in PTP_WORK_CALLBACK pfnwk, __in_out_opt PVOID pv, __in_opt PTP_CALLBACK_ENVIRON pcbe);......
Windows内核线程休眠KeSleep函数
//传入的数据若为1000,则 睡眠的时间为: 1000 * 100 ns * 10 *1000 =1sVOID KeSleep(LONG msec){ #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLI......
SavedApcState与线程挂靠
_KTHREAD线程0x258地址处: +0x258 SavedApcState : _KAPC_STATE +0x258 SavedApcStateFill : [43] UChar此为备份SavedApcState,用于当线程挂靠到别的进程时,保存当前进程的APC State......
PsExitSpecialApc线程的退出示例
在上一节KiInsertQueueApc中,可以看到有一个特殊的APC,其为PsExitSpecialApc,表示线程退出的APC. if (Apc->NormalRoutine) { /* Normal APC; is it the Thread Termin......
窗口与线程的关系
一个GUID线程,会对应一个消息队列。该队列就在THREADINFO结构体中的MessageQueue中。消息的产生与发送SP++会在GetMessage中挂一个钩子,所以会收到消息。键盘 / 鼠标SendMessage/PostMessage系统产生在Win32k.sys中有2个线程Ini......
线程ETHREAD
每个线程在内核都有一个对应的结构体:ETHREAD,ETHREAD的第一个成员为KTHREAD.1: kd> dt _ETHREADnt!_ETHREAD +0x000 Tcb : _KTHREAD +0x5e0 CreateTime : ......
线程等待链表/调度链表
线程3种状态:正在运行就绪等待等待线程链表阻塞的线程在等待线程链表中。KTHREAD结构体中: +0x0d8 WaitListEntry : _LIST_ENTRY +0x0d8 SwapListEntry : _SINGLE_LIST_ENTRY当一个线程处于等待状......
模拟线程切换ThreadSwitch源代码(X86)
ThreadSwitch.cpp// ThreadSwitch.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include "ThreadCor......
Windows内核进程&线程函数大全
1. PEPROCESS NTAPI PsGetCurrentProcess2. HANDLE NTAPI PsGetCurrentProcessId(VOID) 3. variable PsGetCurrentThreadId4. define PsGetCur......
Windows内核线程切换
线程切换的函数是:KiSwapThread,该函数内部调用 KiSwapContext进行堆栈信息的切换。ReactOS的源代码如下:LONG_PTR FASTCALL KiSwapThread(IN PKTHREAD Thread,IN PKPRCB Prcb ) { ......
OSB线程-obs_graphics_thread
根据OSB官方文档介绍,Libobs库有3个线程,分别为:osb_graphic线程,obs_video线程和obs_audio线程。对于任意一个应用软件,其线程架构的设计,可以准确地了解一个应用软件的设计思路,更进一步地理解应用软件的框架设计。另外,理解了此类软件的框架设计,吸取别人优秀的设计......
QT的第一个例程-跨线程操作UI错误
创建QT一般会有两个示例例程,创建方法这里暂不再说明。如果将来本人良心发现,就补一节。第一个示例是创建界面即有mainwindows.ui文件的示例:https://c.biancheng.net/view/1817.html第二个示例是不创建mainwindows.ui示例文件的示例:https:......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!