关于线程的实现细节,我们这里只讨论两个问题。即线程的内核对象和线程栈以及线程的创建过程。上一章我们提到,线程是由线程内核对象以及线程栈两个部分组成的。系统通过线程的内核对象管理线程。线程栈则维护参数和局部变量。
1. 线程的内核对象和线程栈
下表很清楚的表明了内核对象和线程栈的内容以及直接的管理。
调用了CreateThread之后,系统会创建线程的内核对象。内核对象包括一个上下文(用于恢复现场)。从上图可以看出。SP指向了线程栈的首 地址。IP指向了系统提供的一个函数。引用计数是表示当前内核对象的引用次数,当递减到0,内核对象将被删除。挂起计数表示此内核对象被挂起的计数,当减 到0时,线程成为可调度状态。刚刚创建的线程退出码都是STILL_ACTIVE。都是没有信号状态。也许你问一个问题:为什么初始引用计数要等于2呢? 因为线程创建的时候要返回一个句柄,他拥有了一个计数,另一个计数是被创建的新线程自己拥有的。在线程函数返回时递减。弄清楚了这个我们也就清楚了在线程 函数最后返回前是否需要调用_endthread或_endthreadex或调用CloseHandle关闭自己。线程内核对象只有在计数器减到0时才 会被系统销毁。销毁之后再调用与线程句柄相关的函数都可能出错。比如CloseHandle,当然如果传入的是伪句柄不一定,但是可能递减了其他线程的计 数器。
线程的内核对象一旦创建完毕,系统就分配用于线程栈的内存。内存是用进程的地址空间分配来的。然后,系统将两个值写入栈的上端。第一个值是传给 CreateThread的参数(最后传入线程函数),另一个是线程函数的地址。从上图可以看到,内核对象初始化后,IP指向了一个系统的函数 BaseThreadStart而不是用户的线程函数。否则系统就无法在线程函数返回时调用ExitThread函数了。
1. 线程的创建过程
a.建立线程内核对象
b.设置内核对象初始值:比如计数器=2等
c.分配内存给线程栈
d.初始化线程栈:将线程函数和参数push
e.初始化上下文内容:比如IP和SP以及其他CPU寄存器
f. 调用BaseThreadStart函数
g.调用用户线程函数