線程的組成:
線程核心對象和線程堆棧(系統從程序的位址空間中配置設定記憶體給線程棧使用,因為線程沒有自己的位址空間)。
線程還有自己的一組CPU寄存器。
線程1M的棧空間是從程序棧空間中配置設定的,每個線程占用不同的一塊棧空間。如下圖:
線程的建立:
CreateThread()的參數如下:
psa:核心對象的安全屬性(傳入NULL,表示使用預設安全屬性)。
cbStackSize:指定線程棧可使用多少位址空間。
(
線程棧的空間大小,除了通過cbStackSize來設定,也可以通過連結器的/STACK開關來設定。格式如下:
/STACK: [reserve] [,commit]:
reserve:線程棧的記憶體空間(預設為1MB);
commit:線程棧的實體存儲器大小(預設為1個頁面)。
連結器會把該參數嵌入到.exe或.dll的PE頭檔案中。
l 若cbStackSize參數設定為0,系統将根據/STACK來設定預設的棧空間大小;
l 若cbStackSize參數為非0值,配置設定棧空間的時候,按照cbStatckSize和/STACK中比較大的值來配置設定。
)
pfnStartAddr:線程函數的位址;
pvParam:線程開始執行時,将該參數傳給線程函數(給線程函數傳一個初始值);
dwCreateFlags:
l 值為0:線程建立後立即排程;
l 值為CREATE_SUSPENDED:線程建立後挂起(可以線上程函數執行之前,更改線程屬性,再喚醒它);
pdwThreadID:線程ID,線程建立成功,将ID傳給該參數(一般傳入NULL,表示不想知道線程的ID)。
線程的終止:
ExitThread()、TerminateThread()函數。
l 若調用ExitThread來終止線程,該線程的線程棧會被銷毀;
l 若調用TerminateThread來終止線程,隻有當建立該線程的程序退出時,線程棧才被銷毀。
線程終止時,将執行以下操作:
(1) 釋放屬于該線程的使用者對象句柄(線程有兩個使用者對象:視窗和挂鈎);
(2) 設定線程退出代碼;
(3) 線程核心對象變為觸發狀态;
(4) 如果它是程序的最後一個活動線程,那麼程序也随之終止;
(5) 線程核心對象計數減1。
線程的CPU寄存器:
又稱線程上下文(context),上下文反映了當線程上一次執行時,線程的CPU寄存器狀态。(線程的CPU寄存器,儲存在一個CONTEXT結構中,而CONTEXT結構本身儲存線上程的核心對象中)。
上下文中最重要的兩個寄存器:指令指針寄存器、棧指針寄存器。
RtlUserThreadStart()函數:
原型:VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam);
線程核心對象初始化的時候,會調用該函數:
l CONTEXT結構的堆棧指針寄存器被設定為pfnStartAddr線上程堆棧中的位址。
l 指令指針寄存器設定為RtlUserThreadStart函數的位址。
(
RtlUserThreadStart函數就是線程的啟動函數,執行過程類似于程序的啟動函數。
pvParam參數:就是CreateThread()函數傳入的那個參數;
如果線程出現一個未處理異常,将會彈出消息框,并最終通過ExitProcess來終止整個程序,而不隻是終止本線程。
)
_beginthreadex()函數:
l 每個線程有自己專用的_tiddata記憶體塊,是從C/C++運作庫堆棧上配置設定的。線程函數的位址儲存在_tiddata中。
l _beginthreadex函數内部會調用CreateThread函數。
(建議使用_beginthreadex函數,而不用CreateThread來建立線程)
_endthreadex()函數:
l 擷取_tiddata記憶體塊位址,并将其釋放;
l 調用ExitThread銷毀線程。
(不鼓勵使用_endthreadex函數來終止線程,但至少比ExitThread要好點。因為ExitThread不釋放_tiddata記憶體)
僞句柄:
所謂“真正的句柄”,是指能明确、無歧義地标示一個線程的句柄。但有些函數傳回的是僞句柄(如GetCurrentProcess、GetCurrentThread)。
僞句柄有以下特點:
l 僞句柄指向的對象會根據調用者的不同而改變。是以,不能從一個調用者中擷取僞句柄,然後傳給另一個調用者調用,因為這将導緻兩個調用者調用的并不是同一個對象。
l 調用僞句柄,并不會增加核心對象的使用計數。調用CloseHandle()函數并不會關閉核心對象,隻是簡單的忽略,并傳回FALSE。
l 傳遞僞句柄的方法是:将該句柄複制,再傳遞(用DuplicateHandle)。