Loading... # Windows核心编程-学习笔记4 ## 线程基础 每次初始化进程时,系统都会创建一个主线程。对于使用Microsoft c/c++编译器生成的程序,该线程首先会执行c/c++运行库的启动代码,后者调用入口点函数(\_tmain或\_tWinMain),并继续执行,直到入口点函数返回到c/c++运行库的启动代码,后者最终将调用ExitProcess **编写线程函数**: 每个线程都必须有入口点函数,主线程的是\_tmain或\_tWinMain。要在进程中创建辅助线程,也必须要有自己的入口点函数 ~~~c++ DWORD WINAPI ThreadFunc(PVOID pvParam) { DWORD dResult = 0; ... return dwResult; } ~~~ * 线程函数必须返回一个值,作为线程的退出代码 * 线程函数应尽可能使用函数参数和局部变量 **CreateThread** ~~~c++ HANDLE CreateThread( [in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes, [in] SIZE_T dwStackSize, [in] LPTHREAD_START_ROUTINE lpStartAddress, [in, optional] __drv_aliasesMem LPVOID lpParameter, [in] DWORD dwCreationFlags, [out, optional] LPDWORD lpThreadId ); ~~~ 说明: * 调用CreateThread时,系统会创建一个线程内核对象(一个较小的数据结构) * 系统从进程地址空间为线程栈分配内存。新线程在与创建它的线程相同的进程上下文中运行,所以新线程能访问进程的所有内核对象句柄、进程中的所有内存以及同一进程内其他所有线程的栈,同一进程内的多线程相互间通信很方便 参数: * lpThreadAttributes:指向LPSECURITY_ATTRIBUTES结构的指针,要使用线程内核对象的默认安全属性传入NULL;如果希望所有子进程都能继承到这个线程对象的句柄,必须指定一个LPSECURITY_ATTRIBUTES结构,并将该结构的bInheritHandle成员设置为TRUE * dwStackSize:线程栈初始大小,单位字节;为0时使用默认大小 * lpStartAddress:指定希望新线程执行的地址 * lpParameter:传给线程函数 * dwCreationFlags:指定额外的标志来控制线程的创建。值为0,线程创建后可以立即调度;值为CREATE_SUSPEND,系统将创建并初始化线程,但是暂停线程运行调度 * lpThreadId:必须是一个DWORD的有效地址,用于存储系统分配给新线程的ID,一般传NULL **终止线程**: * 线程函数返回 * ExitThread * TerminateThread * 包含线程的进程终止 **线程终止运行时**: * 线程拥有的所有用户对象句柄被释放 * 线程退出代码从STILL_ACTIVE(0x103)变成传给ExitThread或TerminateThread的代码;GetExitCodeThread可以获取线程退出代码 * 线程内核对象的状态变为触发状态 * 如果线程是进程中的最后一个活动线程,系统认为进程也终止了 * 线程内核对象的使用计数递减1 ## 线程内幕 一旦创建线程内核对象,系统就分配内存供线程栈使用。此内存是从进程的地址空间内分配的,因为线程没有自己的地址空间。系统将lpParameter和lpStartAddress,从高往低写内存地址 每个线程都有自己的一组CPU寄存器,称为线程的上下文,在一个CONTEXT结构中。其中最重要的是SP和IP寄存器 当线程的内核对象被初始化时,CONTEXT结构的栈指针寄存器被设为lpStartAddress在线程栈中的地址;指令指针寄存器被设为RtlUserThreadStart函数的地址(NTDLL.dll导出) ~~~c++ VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) { __try { ExitThread(...); } __except(UnhandledExceptionFilter(GetExceptionInformaiton())) { ExitProcess(GetExceptionCode()); } } ~~~ 线程完全初始化好后,系统检查是否向CreateThread函数传递了CREATE_SUSPEND标志,没有则将线程的挂起计数递减至0;随后线程可以调度给一个处理器去执行。然后系统在实际的CPU寄存器中加载上一次在线程上下文中保存的值。现在线程就可以在它的进程地址空间中执行代码并处理数据。 新线程执行RtlUserThreadStart时 * 设置一个结构化异常处理 * 系统调用线程函数,将传给CreateThread的lpParameter传给它 * 线程函数返回时,RtlUserThreadStart调用ExitThread,将线程函数的返回值传给它。线程内核对象的使用计数递减,而后线程停止执行 * 如果线程引发未处理的异常,SEH帧会处理该异常;通常意味着系统会向用户显示一个消息框,用户一旦关闭此消息框,RtlUserThreadStart就会调用ExitProcess终止整个进程 用C/C++写的时候必须调用\_beginthreadx,和CreateThread差不多,参数列表一样,但名称、类型不完全一样 最后修改:2025 年 12 月 16 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏