線程的掛起和恢復
DWORD SuspendThread ( HANDLE hThread ); //掛起線程
DWORD ResumeThread ( HANDLE hThread ); //恢復線程
SuspendThread 和 ResumeThread 都返回之前的掛起計數。
一個線程最多可以掛起MAXIMUM_SUSPEND_COUNT (WinNT.h中定義為127次)。
進程的掛起和恢復
對於Windows來說,不存在暫停或恢復進程的概念,因為進程從來不會被安排獲得cpu時間。
但是我們可以創建一個函數,用來掛起或者恢復進程中的全部線程,這樣就能掛起或者恢復一個進程了。
參考代碼如下:
#include <Windows.h>
#include <stdio.h>
#include <Tlhelp32.h>
//dwProcessID參數為需要掛起或者恢復的進程ID
// bSuspend參數如果為TRUE就掛起進程,否則恢復進程
void SuspendProcess(DWORD dwProcessID, BOOL bSuspend)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwProcessID); //獲得系統內所以線程
if (hSnapshot != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
ZeroMemory(&te, sizeof(te));
te.dwSize = sizeof(te);
BOOL bOK = Thread32First(hSnapshot, &te);
for (; bOK; bOK = Thread32Next(hSnapshot, &te))
{
if (te.th32OwnerProcessID == dwProcessID) //必須制定,否則程序會嘗試掛起系統內所有的線程,就會導致死機
{
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, te.th32ThreadID);
if (hThread != NULL)
{
if (bSuspend)
{
SuspendThread(hThread);
}
else
ResumeThread(hThread);
}
CloseHandle(hThread);
}
}
}
CloseHandle(hSnapshot);
}
int main(void)
{
SuspendProcess(9636, FALSE);
return 0;
}
睡眠
VOID Sleep (DWORD dwMilliseconds);
這個函數將使線程自己掛起 dwMilliseconds 長的時間。
1. 調用Sleep,可使線程自願放棄它剩余的時間片。
2. 系統將在大約的指定毫秒數內使線程不可調度。不錯,如果告訴系統,想睡眠 100ms,那么可以睡眠大約這么長時間,但是也可能睡眠數秒鍾或者數分鍾。記住,Windows不是個實時操作系統。雖然線程可能在規定的時間被喚醒,但是它能否做到,取決於系統中還有什么操作正在進行。
3.可以調用Sleep,並且為dwMilliseconds參數傳遞INFINITE。這將告訴系統永遠不要調度該線程。這不是一件值得去做的事情。最好是讓線程退出,並還原它的堆棧和內核對象。
4. 可以將0傳遞給Sleep。這將告訴系統,調用線程將釋放剩余的時間片,並迫使系統調度另一個線程。但是,系統可以對剛剛調用 Sleep的線程重新調度。Sleep(0)是指CPU交出當前線程的執行權,讓CPU去執行其他線程。也就是放棄當前線程的時間片,轉而執行其他線程
切換到另一個線程
BOOL SwitchToThread (void);
當調用這個函數的時候,系統要查看是否存在一個迫切需要CPU時間的線程。如果沒有線程迫切需要CPU時間,SwitchToThread就會立即返回。如果存在一個迫切需要 CPU時間的線程,SwitchToThread就對該線程進行調度(該線程的優先級可能低於調用 SwitchToThread的線程)。
這個迫切需要CPU時間的線程可以運行一個時間段,然后系統調度程序照常運行。該函數允許一個需要資源的線程強制另一個優先級較低、而目前卻擁有該資源的線程放棄該資源。如果調用 SwitchToThread函數時沒有其他線程能夠運行,那么該函數返回 FALSE,否則返回一個非0值。
Sleep():時間片只能讓給優先級相同或更高的線程;
SwitchToThread():只要有可調度線程,即便優先級較低,也會讓其調度。
在實際上下文中談CONTEXT結構
CONTEXT結構包括以下部分:
CONTEXT_CONTROL:包含CPU的控制寄存器,比如指今指針,堆棧指針,標志和函數返回地址..AX, BX, CX, DX, SI, D
CONTEXT_INTEGER:用於標識CPU的整數寄存器.DS, ES, FS, GS
CONTEXT_FLOATING_POINT:用於標識CPU的浮點寄存器.
CONTEXT_SEGMENTS:用於標識CPU的段寄存器.SS:SP, CS:IP, FLAGS, BP
CONTEXT_DEBUG_REGISTER:用於標識CPU的調試寄存器.
CONTEXT_EXTENDED_REGISTERS:用於標識CPU的擴展寄存器I
CONTEXT_FULL:相當於CONTEXT_CONTROL or CONTEXT_INTEGER or CONTEXT_SEGMENTS,即這三個標志的組合
我們可以使用GetThreadContext函數來查看線程內核對象的內部,並獲取當前CPU寄存器狀態的集合。
BOOL GetThreadContext (
HANDLE hThread,
PCONTEXT pContext);
若要調用該函數,只需指定一個CONTEXT結構,對某些標志(該結構的ContextFlags成員)進行初始化,指明想要收回哪些寄存器,並將該結構的地址傳遞給GetThreadContext 。然后該函數將數據填入你要求的成員。
在調用GetThreadContext函數之前,應該調用SuspendThread,否則,線程可能剛好被調度,這樣一來,線程的上下文就和所獲取的信息不一致了。
示例代碼如下:
CONTEXT Context; //定義一個CONTEXT結構
Context.ContextFlags = CONTEXT_CONTROL; //告訴系統我們想獲取線程控制寄存器的內容
GetThreadContext(hThread, &Context); //調用GetThreadContext獲取相關信息
Ps:在調用GetThreadContext函數之前,必須首先初始化CONTEXT結構的ContextFlags成員。
要獲得線程的所有重要的寄存器(也就是微軟認為最常用的寄存器),應該像下面一樣初始化ContextFlags:
Context.ContextFlags = CONTEXT_FULL;
在WinNT. h頭文件中,定義了CONTEXT_FULL為CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS。
當然,我們還可以通過調用SetThreadContext函數來改變結構中的成員,並把新的寄存器值放回線程的內核對象中
BOOL SetThreadContext (
HANDLE hThread,
CONST CONTEXT *pContext);
同樣,如果要改變哪個線程的上下文,應該先暫停該線程。
CONTEXT Context; //定義一個CONTEXT結構
SuspendThread(hThread); //掛起線程
Context.ContextFlags = CONTEXT_CONTROL; //獲取當前上下文的值
GetThreadContext(hThread, &Context);
Context.Eip = 0x00010000; //Eip字段存儲的是指令指針,現在讓指令指針指向地址 0x00010000;
Context.ContextFlags = CONTEXT_CONTROL;
SetThreadContext(hThread, &Context); //重新設置線程上下文
ResumeThread(hThread); //恢復線程,現在線程開始從0x00010000這個地方開始執行指令
線程的優先級
優先級為0的線程:系統啟動時,會創建一個優先級為0的“頁面清零線程”,它只有在系統中沒有其他可調度線程時,才能調度,用來清除內存中的閑置頁面。
優先級在1 ~ 15之間的線程:一般用戶模式下,線程的優先級都在該范圍。
優先級在16 ~ 30之間的線程:一般是內核線程。
一旦進程運行,便可以通過調用SetPriorityClass來改變自己的優先級
BOOL SetPriorityClass(
HANDLE hProcess
DWORD fdwPriority);
用來獲取進程優先級:
DWORD GetPriorityClass( HANDLE hProcess );
設置和獲取線程的相對優先級:
BOOL SetThreadPriority (
HANDLE hThread,
Int nPriority);
Int GetThreadPriority(HANDLE hThread);
允許或者禁止進程或者線程動態提升自己的優先級:
BOOL SetProcessPriorityBoost(
HANDLE hProcess,
BOOL bDisablePriorityBoost);
BOOL SetThreadPriorityBoost(
HANDLE hThread,
BOOL bDisablePriorityBoost);
判斷當前是不是啟用優先級提升:
BOOL GetProcessPriorityBoost(
HANDLE hProcess,
PBOOL pbDisablePriorityBoost);
BOOL GetThreadPriorityBoost(
HANDLE hThread,
PBOOL pbDisablePriorityBoost);
在多CPU的情況下,我們可以限制某些線程只在可用的cpu的一個子集上運行:
BOOL SetProcessAffinityMask(
HANDLE hProcess,
DWORD_PTR dwProcessAffinityMask);
第一個參數hProcess代表要設置的進程。第二個參數dwProcessAffinityMask是一個位掩碼,代表線程可以在哪些CPU上運行。例如,傳入0x00000005意味着這個進程中的線程可以在CPU 0 和 CPU 2上運行,但是不能在CPU 1 和 CPU 3~31上運行。
獲取進程的關聯性掩碼:
BOOL GetProcessAffinityMask(
HANDLE hProcess,
PDWORD_PTR pdwProcessAffinityMask,
PDWORD_PTR pdwSystemAffinityMask);
設置線程的關聯性掩碼:
BOOL SetThreadAffinityMask(
HANDLE hThread,
DWORD_PTR dwThreadAffinityMask);
有時候強制一個線程只是用特定的某個CPU並不是什么好主意。例如,如果有三個線程都只能使用CPU0,而CPU1,CPU2和CPU3卻無所事事。我們想讓一個線程運行在一個CPU上,但是同時系統也允許他移到另一個空閑的CPU,那就更好了。要給線程設置一個理想的CPU,可以調用如下:
DWORD SetThreadIdealProcessor(
HANDLE hThread,
DWORD dwIdealProcessor);
hThread用於指明要為哪個線程設置首選CPU。
dwIdealProcessor函數不是個位掩碼,它是個從0到31/63,用於指明供線程希望使用的首選CPU 。可以傳遞一個MAXIMUM_PROCESSORS的值(在WinNT.h中定義,在32位操作系統中定義為32,64位操作系統中定義為64),表明線程沒有理想的CPU。如果沒有為該線程設置理想的CPU,那么該函數返回前一個理想的CPU或MAXIMUM_PROCESSORS。