核心編程隨筆7——線程調度和優先級
NOTE0——何為上下文切換
每一個線程都有一個上下文。後者保存在線程的內核對象中。這個上下文反映了線程上一次執行時cpu寄存器的狀態。大約每隔20ms(GetSystemTimeAdjustMent函數第二個參數的返回值),windows都會查看所有當前存在的線程內核對象,這些對象中隻有一些是認為可以調度的。window在可調度的線程內核對象中選擇一個,並將上次保存在線程上下文中的值載入cpu寄存器。這一操作成為上下文切換,可以通過spy++看線程屬性。
說明:怎麼確保某個線程在數據到達串行端口來的1MS之內開始運行。答案:不行。實時操作係統可以。但是window不是實時的。實時操作係統需要對底層的硬件有清楚的了解,從而指導硬盤控製器。鍵盤等的延時。
NOTE1——線程的掛起和恢複
有些線程對象的掛起技術大於0,這意味著該線程已經被掛起。不應該給他調度任何cpu時間。可以通過調用CreateProcess或CreateThread函數並傳入CREATE_SUSPENDED標誌來創建一個被掛起的線程。
可以通過調用ResumeThread函數。傳入調用CreateThread時所返回的線程句柄來實現:
DWORD ResumeThread(Handle hThread);如果成功返回線程前一個掛起計數。否則返回0xffffffff,還可以通過SuspendThread來掛起線程DWORD SuspendThread(Handle hThread);任何線程都可以調用這個函數掛起另一個線程,線程可以將自己掛起。但是他無法自己恢複。實際開發中。應用程序在調用suspendthread時必須小心。因為試圖掛起一個線程時。我們不知道線程在做什麼。例如線程正在分配堆中的內存。線程將鎖定堆。當其他線程要訪問堆的時候。他們的執行將被中止。直到第一個線程恢複。之後又在確切知道目標線程是哪個。而且采用完備的措施避免出現因掛起線程而引起的問或死鎖的時候。調用suspendthread才是安全的。
openThread這個函數將找到線程ID匹配的線程內核對象。並將內核對象的使用計數遞增1,然後返回對象的句柄。有了這個句柄。就可以調用suspendthread或resumethread了
NOTE2——切換到另一個線程
SwitchToThread的函數。如果存在另一個可調度線程。那麼係統會讓此線程運行。調用這個函數時。係統查看是否存在正急需cpu時間的饑餓線程。如果沒有立即返回。如果存在.switchtothread將調用該線程。(該優先級可能比swithtothread的主調線程低),
NOTE3——在實際上下文中談context結構。
係統使用context結構記住線程的狀態。這樣線程在下一次獲得cpu可以運行時。就可以在上次停止處繼續。window實際上允許我們查看線程內核對象的內部。並獲得當前cpu寄存器狀態的集合。為此。隻需要調用GetThreadContext:調用它之前應該先調用suspendthread,否則。係統可能正好獲得調度此線程。這樣一來。線程的上下文和所獲得的信息就不一致了。一個線程實際有兩個上下文。用戶模式和內核模式。GetThreadContext隻能返回用戶模式上下文。
通過setthreadcontext來改變結構中的成員。並把新的寄存器值放回線程的內核對象中。同樣如果要改變哪個線程的上下文。應該要暫停該線程。否則結果無法預料。
#include "windows.h"
#include<TLHELP32.H>
#include "stdio.h"
#include "winnt.h"
void SuspendProcess(DWORD dwProcessId,BOOL fsuspend);
int WINAPI WinMain(HINSTANCE hinstExe, HINSTANCE,
PSTR pszCmdLine, int nCmdShow)
{
HANDLE snapshot;
PROCESSENTRY32 processinfo ;
processinfo.dwSize=sizeof(processinfo) ;
snapshot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
if(snapshot==NULL)
return FALSE;
BOOL status= Process32First(snapshot,&processinfo);
while(status)
{
if(stricmp("notepad.exe",processinfo.szExeFile)==0)
break;
status=Process32Next(snapshot,&processinfo);
}
if(status==0)
{
MessageBox(NULL,"沒有找到你要掛起的進程","提示",MB_OK);
return 1;
}
CloseHandle(snapshot);
SuspendProcess(processinfo.th32ProcessID,1);
SuspendProcess(processinfo.th32ProcessID,0);
return 1;
}
void SuspendProcess(DWORD dwProcessId,BOOL fsuspend)
{
HANDLE snapshot;
snapshot=CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD,0);
THREADENTRY32 te={ sizeof(te)};
BOOL fOK=Thread32First(snapshot,&te);
for(;fOK;fOK=Thread32Next(snapshot,&te))
{
if(te.th32OwnerProcessID==dwProcessId)
{
typedef HANDLE (__stdcall *OPENTHREAD) (DWORD dwFlag, BOOL bUnknow, DWORD dwThreadId);
HMODULE hDll =::LoadLibrary("Kernel32.dll");
OPENTHREAD lpfnOpenThread = (OPENTHREAD)::GetProcAddress(hDll, "OpenThread");
HANDLE hThread = lpfnOpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
// HANDLE hThread = lpfnOpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if(fsuspend==1)
{
SuspendThread(hThread);
CONTEXT context;
context.ContextFlags=CONTEXT_FULL;
GetThreadContext(hThread,&context);
context.Eip=0x00010000;
context.ContextFlags=CONTEXT_CONTROL;
SetThreadContext(hThread,&context);
}
else
ResumeThread(hThread);
// HANDLE hThread=OpenThread (THREAD_SUSPEND_RESUME,FALSE,te.th32OwnerProcessID);
}
}
CloseHandle(snapshot);
}
NOTE4——優先級編程
調用createprocess中可以在fdwcreate參數中傳入需要的優先級。一旦進程運行。可以通過setpriorityclass來改變自己的優先級
SetPriorityClass進行設置
SetPriorityClass
設置優先權
函數原型:
BOOL SetPriorityClass( HANDLE hProcess, DWORD dwPriorityClass);
GetPriorityClass
得到優先權
函數原型:
DWORD GetPriorityClass(
HANDLE hProcess
);
dwPriorityClass:
ABOVE_NORMAL_PRIORITY_CLASS 0x00008000
BELOW_NORMAL_PRIORITY_CLASS 0x00004000
HIGH_PRIORITY_CLASS 0x00000080
IDLE_PRIORITY_CLASS 0x00000040
NORMAL_PRIORITY_CLASS 0x00000020
REALTIME_PRIORITY_CLASS 0x00000100
例:
if (!CreateProcess("D:\\Program Files\\TTPlayer\\TTPlayer.exe", GetCommandLine(), NULL,
NULL, FALSE, CREATE_DEFAULT_ERROR_MODE | NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi))
{
CloseHandle(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
}
BOOL fTemp = SetPriorityClass(pi.hProcess, HIGH_PRIORITY_CLASS);
//BOOL fTemp = SetPriorityClass(pi.hProcess, 21);
LONG lRet = GetPriorityClass(pi.hProcess);
用來獲取進程優先級的相應函數如下。getpriorityclass
NOTE5
SetProcessPriorityBoost允許或禁止係統提升一個進程中所有線程的優先級。
而SetThreadPriorityBoost則允許或禁止提升某個線程的優先級。有相應的Get函數來判斷當前時候啟用優先級提升。
SetFileInformationByHandle:Sets the file information for the specified file.BOOL WINAPI SetFileInformationByHandle(
__in HANDLE hFile,
__in FILE_INFO_BY_HANDLE_CLASS FileInformationClass,
__in LPVOID lpFileInformation,
__in DWORD dwBufferSize
);
係統在啟動時將確定計算機中存在多少個cpu。應用程序可以通過調用GetsystemInfo來查詢機器上cpu的數量。如果限製某些進程隻在可用cpu的一個子集上運行。則可以調用SetProcessAffinityMask
The SetProcessAffinityMask function sets a processor affinity mask for the threads of the specified process.
BOOL SetProcessAffinityMask(
HANDLE hProcess,
DWORD_PTR dwProcessAffinityMask
);













































SetThreadAffinityMask是設置此線程隻能在某個或某些處理器上運行;SetThreadIdealProcessor是在上述設置的範圍內,指定線程優先在某個處理器上運行。
當vista在x86計算機上啟動時。我們可以限製係統將所有的cpu數量。在啟動過程中。係統將檢查啟動配置數據(BCD),BCD是一個取代老的boot.ini文本文件的數據存庫,他在計算機的硬件和固件之上提供了一個抽象層。BCD的編程配置是通過wmi實現的。
最後更新:2017-04-03 15:21:57