windows线程内幕
以下内容引述至《windows核心编程》
创建和初始化
CreateThread函数的一个调用导致系统创建一个线程内核对象。该对象的初始化引用计数为2,(除非线程终止,而且从CreateThread返回的句柄关闭,否则线程内核对象不会被销毁)
一旦创建了内核对象,系统就分配内存,提供给线程的堆栈使用。内存是从进程的地址空间内分配的,因为线程没有自己的地址空间。然后,系统将两个值写入新线程堆栈的最上端(线程堆栈始终是从高为位内存向低位内存地址构建的)
每个线程都有自己的一组CPU寄存器,称为线程的上下文(context)。上下文反映了当线程的上一次执行时,线程的CPU寄存器的状态。线程的CPU寄存器全部保存在一个context结构(在WinNT.h头文件中定义),结构本身保存在线程内核对象中。
指令指针寄存器和栈指针寄存器是线程上下文中最重要的两个寄存器。线程始终在进程的上下文中运行。所以,这两个寄存器地址标识的内存都位于线程所在进程的地址空间中。
线程启动
当线程的内核对象被初始化后,context结构的堆栈指针寄存器被设为pfnStartAddr在线程堆栈中的地址。而指令指针寄存器被设为RtlUserThreadStart函数(NTDLL.dll模块)的地址。
线程完全初始化后,系统将检查CREATE_SUSPENDED标识是否已被传给CreateThread函数。
如果CREATE_SUSPENDED标志没有被传递,系统将线程的挂起计数递减至0;随后,线程就可以调度给一个处理器去执行,然后,系统在实际的CPU寄存器中加载上一次在线程上下文中保存的值。线程可以在其进程的地址空间执行代码并处理数据。
线程执行RtlUserThreadStart函数时,将发生以下事情:
- 围绕线程函数,会设置一个结构化异常处理(SEH)帧。这样一来,线程执行期间所产生的任何异常都能得到系统的默认处理;
- 系统调用线程函数,把传给CreateThread函数的pvParam参数传给他;
- 线程函数返回时,RtlUserThreadStart调用ExitThread,获取线程函数的返回值。线程内核对象的引用计数递减,而后线程停止执行
- 如果线程产生了一个未被处理的异常,RtlUserThreadStart函数所设置的SEH帧会处理这个异常。
在RtlUserThreadStart内,线程会调用ExitThread或者ExitProcess,这意味着线程永远不能退出此函数;它始终在其内部“消亡”,所有RtlUserThreadStart函数返回值为void。
一般情况下,RtlUserThreadStart函数时不允许返回的。如果它没有在强行“杀死”线程的前提下尝试返回,几乎肯定引起访问违规,因为线程堆栈上没有函数返回地址,RtlUserThreadStart将尝试返回某个随机的内存位置。
在进程的主线程初始化时,其指令指针会被设为同一个未文档化的函数RtlUserThreadStart。开始执行时,它会调用C/C++运行库的启动代码,后者初始化继而调用你的_tmain或_tWinMain函数。在入口点函数返回时,C/C++运行时启动代码会调用ExitProcess,所以对于C/C++应用程序来说,主线程永远不会返回到RtlUserThreadStart函数。