天天看點

《Windows核心程式設計》讀書心得——線程(線程基礎)(4)

線程的組成:

線程核心對象和線程堆棧(系統從程序的位址空間中配置設定記憶體給線程棧使用,因為線程沒有自己的位址空間)。

線程還有自己的一組CPU寄存器。

線程1M的棧空間是從程序棧空間中配置設定的,每個線程占用不同的一塊棧空間。如下圖:

《Windows核心程式設計》讀書心得——線程(線程基礎)(4)
《Windows核心程式設計》讀書心得——線程(線程基礎)(4)
《Windows核心程式設計》讀書心得——線程(線程基礎)(4)

線程的建立:

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)。

繼續閱讀