## 線程結構
如上一篇文章所述,系統創建線程時,會分配一個內核對象與線程棧。如下圖

**線程內核對象如圖左側,其初始為**
1、引用計數為2
2、掛起計數為1(此時線程無法運行,當線程初始化好后,若未設置CREATE_SUSPENDED標志,則系統會自動將掛起計數減至0,線程為可調度狀態)
3、退出代碼為STILL_ACTIVE狀態
4、內核對象未觸發狀態
5、記錄線程上下文的CONTEXT結構為初始值(所謂線程切換,其實就是根據CONTEXT結構數據更新CPU寄存器內容)。注意其中的SP(棧指針寄存器)與IP(指令指針寄存器)。
SP指向pfnStartAddr而IP指向NTDLL.dll導出的
RtlUserThreadStart函數。這說明,其實每個新建的線程,其運行入口并不是我們傳入的線程函數,而是統一會由系統調用RtlUserThreadStart。
RtlUserThreadStart函數定義如下
~~~
VOID RtlUserStartThread(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParm)
{
__try
{
ExitThread((pfnStartAddr)(pvParm));
}
__except(UnhandledExceptionFilter(GetExceptionInformation()))
{
ExitProcess(GetExceptionCode());
}
// Note: We never get here
}
~~~
**觀察RtlUserThreadStart函數,可得到如下事實:**
1、RtlUserThreadStart函數最終調用ExitThread函數退出線程并設置退出碼。
2、若線程運行期由任何異常,則會被捕獲并結束整個進程。
3、RtlUserThreadStart只會被操作系統調用來開啟線程。
4、RtlUserThreadStart會為線程的返回地址壓棧,讓線程可返回。但RtlUserThreadStart本身永遠不會返回,因為在函數返回前,其線程已經結束(如代碼中注釋一樣)。
5、當進程運行主線程時,RtlUserThreadStart會調用C/C++的運行庫啟動代碼,并有啟動代碼調用main函數,當線程由main返回時,C/C++啟動代碼會調用ExitProcess退出進程。
**線程棧如圖右側所示。**
1、線程棧空間來自進程空間。
2、線程棧空間由高向低擴展。
3、線程棧系統會默認寫入兩個值,分別是CreateThread時傳入的線程參數與線程函數地址。