1. 程式人生 > >Windows核心程式設計筆記(5)----執行緒排程,優先順序

Windows核心程式設計筆記(5)----執行緒排程,優先順序

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函式告訴系統執行緒在多少時間內不需要排程,執行緒自動放棄CPU時間片剩餘時間。由於Windows並不是實時作業系統
睡眠時間不一定準。
Sleep(0)則是告訴系統,當前執行緒主動放棄CPU時間片剩餘時間,強制系統呼叫其他執行緒。如果沒有相同或者優先順序更高的
可排程執行緒,系統會重新排程這個執行緒。

4、切換到另一個執行緒

SwitchToThread切換執行緒,系統檢視是否有正急需CPU的執行緒。如果沒有,函式立即返回;如果存在,SwitchToThread將
排程該執行緒,繼續執行的執行緒可以執行一個CPU時間片,然後系統排程恢復原執行緒。
SwitchToThread與Sleep(0)
SwitchToThread允許執行低優先順序的執行緒,Sleep(0)則是即使有優先順序低的飢餓執行緒也不會被排程。

5、執行緒上下文結構CONTEXT

系統使用CONTEXT記錄執行緒的狀態,這樣線上程下次執行時,就可以從上下文停止處繼續執行。
在Windows定義的所有結構體中,CONTEXT是唯一一個特定於CPU的。
可以使用GetThreadContext獲取執行緒核心的上下文,使用前需要先掛起執行緒,否則獲取的CONTEXT資訊就與當前執行執行緒的
上下文不一致了。一個執行緒有兩個上下文模式:使用者模式和核心模式,GetThreadContext獲取的是使用者模式的上下文資訊。

6、

較高優先順序的執行緒總會搶佔較低優先順序的執行緒,無論低優先順序執行緒是否正在執行。系統啟動時,將建立一個頁面清零執行緒(

zero page thread),這個執行緒的優先順序為0。頁面清零執行緒負責在沒有其他執行緒需要執行時,將系統記憶體中的所有閒置頁
面清零。

7、排程I\O請求優先順序

如果一個低優先順序的執行緒獲得CPU時間,可以很輕易在短時間內將成百上千個I\O請求入列,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);