Windows核心程式設計筆記(5)----執行緒排程,優先順序
阿新 • • 發佈:2019-02-08
1、作業系統執行緒排程過程
每個執行緒都有一個上下文CONTEXT結構體,儲存線上程的核心物件中,這個上下文中儲存了執行緒上一次執行時CPU暫存器的狀態。每隔固定時間,Windows會檢視所有當前存在的執行緒核心物件,其中只有一些是可排程的。Windows在可排程的
執行緒中選擇一個,並將上次儲存到執行緒上下文中的資料載入CPU暫存器中。(上下文切換)
CPU時間片到後,Windows移出這個執行緒,把CPU暫存器資訊儲存到執行緒上下文中,切換到另一個執行緒,如此迴圈。
2、執行緒的掛起和恢復
呼叫CreateProcess或者CreateThread時,系統將建立執行緒核心物件,並將掛起計數初始化為1,這樣系統就不會給這個線程排程CPU了。線上程初始化完成後,函式將檢視是否有CREATE_SUSPENDED標誌傳入。如果有,函式返回並讓新的執行緒處於
掛起狀態;沒有,函式會將執行緒的掛起計數遞減為0,掛起計數為0時,執行緒就成為可排程的了。
使用ResumeThread函式恢復執行緒至可排程狀態,該函式返回執行緒的前一個掛起計數,失敗返回0xffffffff。
SuspendThread掛起執行緒,一個執行緒最多可以掛起127次。SuspendThread是非同步的,只有在確切知道執行緒正在做什麼,而且
採用完備措施避免出現因掛起執行緒而引起的問題或者死鎖時,呼叫SuspendThread才是安全的。
3、睡眠
睡眠時間不一定準。
Sleep(0)則是告訴系統,當前執行緒主動放棄CPU時間片剩餘時間,強制系統呼叫其他執行緒。如果沒有相同或者優先順序更高的
可排程執行緒,系統會重新排程這個執行緒。
4、切換到另一個執行緒
SwitchToThread切換執行緒,系統檢視是否有正急需CPU的執行緒。如果沒有,函式立即返回;如果存在,SwitchToThread將排程該執行緒,繼續執行的執行緒可以執行一個CPU時間片,然後系統排程恢復原執行緒。
SwitchToThread與Sleep(0)
SwitchToThread允許執行低優先順序的執行緒,Sleep(0)則是即使有優先順序低的飢餓執行緒也不會被排程。
5、執行緒上下文結構CONTEXT
在Windows定義的所有結構體中,CONTEXT是唯一一個特定於CPU的。
可以使用GetThreadContext獲取執行緒核心的上下文,使用前需要先掛起執行緒,否則獲取的CONTEXT資訊就與當前執行執行緒的
上下文不一致了。一個執行緒有兩個上下文模式:使用者模式和核心模式,GetThreadContext獲取的是使用者模式的上下文資訊。
6、
較高優先順序的執行緒總會搶佔較低優先順序的執行緒,無論低優先順序執行緒是否正在執行。系統啟動時,將建立一個頁面清零執行緒(
zero page thread),這個執行緒的優先順序為0。頁面清零執行緒負責在沒有其他執行緒需要執行時,將系統記憶體中的所有閒置頁面清零。
7、排程I\O請求優先順序
此低優先順序執行緒可能會掛起高優先順序執行緒,使它們無法完成任務,從而顯著影響系統的響應性。
可以通過SetThreadPriority傳入THREAD_MODE_BACKGROUND_BEGIN屬性,告訴Windows執行緒應該傳送低優先順序的I\O請求,這
也將降低執行緒的CPU排程優先順序。通過設定THREAD_MODE_BACKGROUND_END屬性,可以恢復執行緒的優先順序。
系統不允許執行緒改變另一個執行緒的I\O請求優先順序。
同樣的,可以通過SetPriorityClass傳入PROCESS_MODE_BACKGROUND_BEGIN屬性,設定程序內所有執行緒進行低優先順序的I\O
請求和CPU排程。PROCESS_MODE_BACKGROUND_END屬性來恢復。
系統不允許程序改變另一個程序的I\O請求優先順序。
測試程式碼
掛起一個程序裡的所有執行緒,使用需注意(可能在遍歷後有新的執行緒建立、銷燬)
void SuspendProcess(DWORD dwPid, BOOL bSuspend)
{
HANDLE hSnapshort = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwPid);
if ( INVALID_HANDLE_VALUE != hSnapshort )
{
THREADENTRY32 te = { sizeof(THREADENTRY32) };
BOOL bRet = Thread32First(hSnapshort, &te);
while( bRet )
{
if ( te.th32OwnerProcessID == dwPid )
{
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
if ( NULL == hThread )
continue;
if ( bSuspend )
SuspendThread(hThread);
else
ResumeThread(hThread);
CloseHandle(hThread);
}
bRet = Thread32Next(hSnapshort, &te);
}
CloseHandle(hSnapshort);
}
}
設定執行緒優先順序,獲取執行緒上下文資訊
HANDLE hThread = CreateThread(NULL, 0, TestThread, 0, CREATE_SUSPENDED, NULL);
SetThreadPriority(hThread, THREAD_PRIORITY_IDLE|THREAD_MODE_BACKGROUND_BEGIN);
ResumeThread(hThread);
CloseHandle(hThread);
SuspendThread(hThread);
SuspendThread(hThread);
DWORD dwCount = ResumeThread(hThread);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
SuspendProcess(316, TRUE);
SuspendProcess(316, FALSE);
CONTEXT context;
memset(&context, 0, sizeof(CONTEXT));
context.ContextFlags = CONTEXT_INTEGER;//CONTEXT_CONTROL;
SuspendThread(GetCurrentThread());
GetThreadContext(GetCurrentThread(), &context);