Windows Via C/C++ 讀書筆記 3

Windows Via C/C++ 讀書筆記 

1. Job

JOB,翻譯成工作或者任務。JOB是管理多個進程的集合體。如果你需要一次關閉多個進程,並且要在所有進程退出後得到通知,那麼可以使用JOB這種對象。

1.1. Job的使用流程

1. 創建JOB或查找一個已有的JOB

2. 把進程加入到JOB

3. 關閉JOB

4. 等待JOB結束(JOB結束後會處於Signaled狀態,因此可以用WaitForSingleObject 等待)

2. 線程

線程含有兩個東西:

一個內核對象,用於操作系統管理,含有線程的靜態信息。和進程內核對象類似。

線程這個東西用了太久了,就不廢話了。揀點以前沒注意的死角說說。

2.1. 線程棧(Thread`s Stack)。

線程棧是有大小的,默認是1M。超過的時候會有"overflow"異常,但是操作系統會捕捉異常,給棧更多的空間,使棧能動態增長。

創建線程的時候可以通過參數設置棧大小,也可以在link選項/STACK中設置,最終取兩者中大的一個。 

2.2. 線程結束

和進程結束太類似了。有點要注意:如果用"TerminateThread" 殺死另外一個線程,操作系統是不會銷燬它的棧,直到這個進程結束。

"GetExitCodeThread"可以得到線程的退出碼,如果線程沒有退出,那麼這個值是"STILL_ACTIVE" 

2.3. 線程內部

線程內核對象:

SP(Stack Pointer)指向棧的頭,棧頂是線程函數的參數,接着是執行代碼的起始地址。

IP(Instruction Pointer) 指向函數"RtluserThreadStart",該函數在"NTDLL.dll"中導出。

還保留了線程執行時CPU寄存器的狀態,用於線程切換的時候,可以恢復切換前的執行狀態。

VOID RtlUserThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) {

   __try {

      ExitThread((pfnStartAddr)(pvParam));

   }

   __except(UnhandledExceptionFilter(GetExceptionInformation())) {

      ExitProcess(GetExceptionCode());

   }

   // NOTE: We never get here.

}

RtlUserThreadStart原型

線程執行肯定會在RtlUserThreadStart中結束,正常結束調用ExitThread(線程結束),異常調用ExitProcess(進程結束),並且會彈出窗口報錯。

該函數的第一個參數,函數地址參數是操作系統顯示寫入線程stack中,使該函數看起來像是被別人調用,實際上它是起始執行函數。

2.4. _beginthreadex & _endthreadex

2.4.1. _beginthreadex代替CreateThread

多線程的時候,有些全局變量的訪問會衝突。如:errno, _doserrno, strtok_wcstok, strerror, _strerrortmpnam, tmpfile, asctime_wasctime, gmtime, _ecvt, and _fcvt

爲了讓各個線程有自己的變量,用_beginthreadex代替CreateThread函數。

_beginthreadex函數中會給每個線程創建一個變量塊(類型爲_tiddata,這個結構存了這些errno什麼的,還有線程函數的入口地址)

下面都是僞代碼,描述函數主要過程:

_beginthreadex 

{

......

分配一塊內存,結構爲_tiddata,變量名爲pid;

創建一個線程(CreateThread),函數爲_threadstartex,把pid作爲參數傳給這個函數;

......

}

_threadstartex

{

......

pidTSL關聯,變成與線程相關的變量

Try{

執行線程函數,線程函數地址存在pid

執行_endthreadex結束線程,把線程函數執行退出結果作爲_endthreadex的參數。

}catch

{

.......

}

......

}

_endthreadex

{

釋放TSL變量pid

調用ExitThread退出線程

}

如過用CreateThread代替_beginthreadex,會有什麼問題呢?

1.操作系統會在第一次使用pid的時候檢查是否已經分配,如果沒有會創建一個新的並關聯。但是因爲沒有調用_endthreadex,這個變量不會被釋放直到進程退出。

2.原文是First, if the thread uses the C/C++ run-time library's signal function, the entire process terminates because the structured exception handling frame has not been prepared.

這塊還不清楚,在_beginthreadextry塊會處理一些異常,如果用CreateThread這種異常沒有捕捉會使整個進程退出。

2.5. 不要使用_beginthread 和 _endthread

因爲這兩個函數的參數少,因此功能會比ex版本的函數功能弱,比如不能帶安全參數,不能帶suspend標誌,不能返回線程號。_endthread只能使退出碼硬編碼爲0

_endthread會關閉線程句柄,因此線程結束後線程句柄不能訪問,而ex版本的不會,例如下面這段代碼,線程句柄在_beginthread後可能已經關閉了,後面的訪問會出錯。

DWORD dwExitCode;

HANDLE hThread = _beginthread(...);

GetExitCodeThread(hThread, &dwExitCode); //error!

CloseHandle(hThread);

2.6. DuplicateHandle產生真實句柄

The GetCurrentThread function retrieves a pseudo handle for the current thread.

因此需要用函數轉換一下,否則不能被其它函數使用,也不能傳遞給其它線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章