Windows Internals 筆記——執行緒優先順序
1.每個執行緒都被賦予0(最低)~31(最高)的優先順序數。當系統確定給哪個執行緒分配CPU時,它會首先檢視優先順序為31的執行緒,並以迴圈的方式進行排程。如果有優先順序為31的執行緒可供排程,那麼系統就會將CPU分配給該執行緒。在該執行緒的時間片結束時,系統檢視是否還存在另一個優先順序為31的執行緒可以執行,如果存在,它將獲得CPU。
2.只要有優先順序為31的執行緒可供排程,系統就不會給優先順序0~30的執行緒分配CPU。這種情況稱為飢餓。在多處理器機器上飢餓發生的可能性要小得多,因為這種機器上優先順序為31和30的執行緒可以同時執行。
3.較高優先順序的執行緒總是會搶佔較低優先順序的執行緒,無論較低優先順序的執行緒是否正在執行。系統確定有較高優先順序的執行緒已經準備好可以執行時,它會立即暫停較低優先順序的執行緒(即使後者的時間片還沒有用完),並將CPU分配給較高優先順序的執行緒,該執行緒獲得一個完整的時間片。
4.系統啟動時,將建立一個名為頁面清零執行緒的特殊執行緒。這個執行緒的優先順序定為0,而且是整個系統中唯一一個優先順序為0的執行緒。頁面清零執行緒負責在沒有其他程序需要執行的時候,將系統記憶體中的所有閒置頁面清零。
5.Windows API在系統的排程程式之上提供了一個抽象層,因此我們不會直接排程排程程式,相反,呼叫的是Windows函式,它們會根據底層作業系統的版本來解釋引數。
6.Windows支援6個優先順序類:
- idle優先順序類非常適合只在系統什麼都不做的時候執行的應用程式。
- 只有在絕對必要的時候才使用high優先順序。應該儘可能避免使用real-time優先順序類,因為大多數作業系統執行緒在執行時所用的優先順序類都比它低。
- 99%的程序都是normal優先順序類。
- 程序不能執行在real-time優先順序類,除非使用者有Increase Scheduling Priority特權。預設情況下,隸屬於管理員或者高階使用者組的使用者都具有這一許可權。
7.Windows支援7個相對執行緒優先順序,這些優先順序是相對於程序優先順序的。同樣,大多數執行緒使用normal優先順序。
8.概括起來,程序都屬於某個優先順序類,另外還可以指定程序中執行緒的相對執行緒優先順序。 在不同版本的作業系統上對映(對應到0~31)是變化的。如下表是Windows Vista上的具體情況。
9.優先順序0是保留給頁面清零執行緒。除此之外,應用程式也無法獲得一下優先順序:17,18,19,20,21,27,28,29,30。但是如果編寫的是執行在核心模式的裝置驅動程式,那麼我們可以獲得這些優先順序。使用者模式的應用程式是不能獲得這些優先順序的。
10.real-time優先順序類的執行緒,其優先順序值不能低於16。同理,非real-time優先順序執行緒的優先順序的優先順序值不能高於15。
11.一般而言,有較高優先順序的執行緒大多數時候都應是不可排程的,當這種執行緒要執行什麼任務時,很快就能得到CPU時間。這時,執行緒應該儘可能少地執行CPU指令,並重新進入睡眠,等待再次被排程。相反,優先順序低的可以保持為可排程狀態,執行大量CPU指令以完成其任務。
12.呼叫CreateProcess時,可以在fdwCreate引數中傳入需要的優先順序。
13.一旦程序執行,可以呼叫SetPriorityClass來改變自己的優先順序。GetPrioirityClass來獲取程序優先順序。
14.通過命令列介面呼叫程式時,程式的起始優先順序時normal。但是,如果使用START命令呼叫程式,可以使用一個開關指定程式的起始優先順序。例如:
C:\WINDOWS\system32>START /LOW CALC.EXE
15.CreateThread函式沒有為呼叫者提供設定新執行緒相對優先順序的辦法。為了設定和獲取執行緒的相對優先順序,必須呼叫SetThreadPriority和GetThreadPriority函式。
16.CreateThread總是建立相對執行緒優先順序為normal的新執行緒。要使執行緒以idle優先順序執行,我們需要在呼叫CreateThead上傳入CREATE_SUSPENDED標誌,這將阻止執行緒執行任何程式碼。然後呼叫SetThreadPriority將執行緒改為idle相對執行緒優先順序。接著呼叫ResumeThread,執行緒就成為可排程的了。
17.Windows並沒有提供返回執行緒優先順序的函式,因為Microsoft保留了任何時候改變排程演算法的權力。
18.偶爾,系統也會提升一個執行緒的優先順序,通常時為了響應某種I/O事件,比如視窗訊息或磁碟讀取。例如:
- high優先順序程序中的一個執行緒優先順序為normal的執行緒,其基本優先順序值為13.如果使用者敲一個鍵,系統會線上程的佇列中放入一個WM_KEYDOWN訊息。因為有訊息出現線上程的佇列中,執行緒就成為可排程的了。而且,鍵盤裝置驅動程式將使用系統臨時提升執行緒的優先順序。因此執行緒的優先順序可能會提升2,從而達到15。
- 執行緒在優先順序為15時分得一個時間片。在該時間片結束之後系統將執行緒的優先順序值減1,所以在下一個時間片中執行緒的優先順序將為14.執行緒的第三個時間片以優先順序13執行。以後的時間片將保持在13,即執行緒的基本優先順序。
注意,執行緒的當前優先順序不會低於執行緒的基本優先順序。而且使執行緒可排程的裝置驅動程式能夠決定提升的幅度,同樣,Microsoft也沒有在文件中記錄任何一個裝置驅動程式能夠將執行緒的優先順序提升多少。因此,Microsoft可以不斷地微調動態提升,以確定最佳的總體響應性。
19.系統只提升優先順序值在1~15的執行緒。這個範圍被稱為動態優先順序範圍。而且,系統不會吧執行緒的優先順序提升到實施範圍(高於15)。系統也不能動態提升實時範圍(16~31)的執行緒。
20.Microsoft增加SetProcessPriorityBoost和SetThreadPriorityBoost兩個函式,允許我們禁止系統對執行緒優先順序進行動態提升。GetProcessPriorityBoost和GetThreadPriorityBoost來查詢。
21.當系統檢測到有執行緒已經處於飢餓狀態3到4秒,它會動態將飢餓執行緒的優先順序提升到15,並允許該執行緒執行兩個時間片。當兩個時間片結束時,執行緒的優先順序立即恢復到基本優先順序。
22.如果使用者需要使用某個程序的視窗,這個程序就稱為前臺程序,所有其他程序稱為後臺程序。為了改進前臺程序的響應性,Windows會為前臺程序中的現初微調排程演算法。系統給前臺程序的執行緒分配比一般情況下更多的時間片。這種微調只在前臺程序是normal優先順序時才進行。如果處於其他優先順序,則不會進行微調。
23.從Windows Vista開始,執行緒可以在進行I/O請求時設定優先順序。我們可以通過調研SetThreadPriority並傳入THREAD_MODE_BACKGROUND_BEGIN來告訴Windows,執行緒應該傳送低優先順序的I/O請求。注意,這也將降低執行緒的CPU排程優先順序。我們可以通過呼叫SetThreadPriority並傳入THREAD_MODE_BACKGROUND_END,讓執行緒進行normal優先順序I/O的請求。系統不允許執行緒改變另一個執行緒的I/O優先順序。
24.如果想讓程序中的所有執行緒都進行低優先順序的I/O請求和低CPU排程,那麼我們可以呼叫SetPriorityClass,傳入THREAD_MODE_BACKGROUND_BEGIN標誌,相反則傳入THREAD_MODE_BACKGROUND_END。
25.在更細的粒度上,normal優先順序執行緒還可以執行對某個檔案執行後臺優先順序I/O。例如SetFileInformationByHandle設定的優先順序將覆蓋程序的優先順序或執行緒。
26.如果一個low優先順序執行緒獲得了normal優先順序執行緒等待的鎖,則normal優先順序執行緒可以在低優先順序I/O請求完成之前執行,不等待後臺優先順序執行緒。後臺優先順序執行緒甚至不再進行I/O,以免出現問題。因此,應該儘量減少在normal和後臺優先順序執行緒之間使用共享同步物件,以避免normal優先順序執行緒為後臺優先順序執行緒擁有的鎖而被阻塞,導致優先順序逆轉。