天天看點

Windows 核心程式設計 學習筆記 (第三部分)

目錄

16. 作業

  •     作業
  •     一個簡單例程
  •     CreateJobObject 建立作業
  •     作業限制和 SetInformationJobObject
  •     AssignProcessToJobObject 将程序添加到作業
  •     終止作業
  •     QueryInformationJobObject 查詢作業的統計資訊
  •     作業的通知消息

17.  線程

  •     線程
  •     線程的回調函數(入口函數)
  •     CreateThread和_beginthreadex的差別
  •     CreateThread 建立線程
  •     線程的終止
  •     線程建立和初始化的細節
  •     _beginthreadex函數

18. 線程排程

  •     線程挂起和恢複
  •     Sleep函數
  •     切換線程
  •     線程執行時間
  •     CONTEXT上下文
  •     Windows線程排程
  •     程序優先級
  •     線程相對優先級
  •     動态提高線程的優先級
  •     親緣性

16. 作業

(1) 作業

[MSDN] 作業對象允許一組程序被當做一個單元進行管理。作業對象是可命名的、安全的、共享的對象,它能夠控制它包含的所有程序的屬性。執行在作業上的操作會影響作業包含的所有程序。

作業可視為程序的容器,可以對其中的所有程序加上限制條件。

使用CrateJobObject函數,建立一個作業

使用SetInformationJobObject函數,為作業添加限制條件

使用AssignProcessToJobObject函數,将程序添加到作業

使用IsProcessInJob函數,判斷一個程序是否屬于一個作業。

(2) 一個簡單例程

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85

#include <windows.h>

#include <tchar.h>

int

WINAPI wWinMain(

HINSTANCE

hInstance,

HINSTANCE

,

PWSTR

lpCmdLine,

int

nShowCmd){

//建立一個作業核心對象

HANDLE

hJob = CreateJobObject(NULL,NULL);

//

//為作業添加一些基本限制

//基本限制結構體

JOBOBJECT_BASIC_LIMIT_INFORMATION jobli = {0};

//作業的優先級

jobli.PriorityClass = IDLE_PRIORITY_CLASS;

//

//作業的CPU時間限制

jobli.PerJobUserTimeLimit.QuadPart = 10000000;

//1秒,機關是100納秒

//指明限制條件

jobli.LimitFlags = JOB_OBJECT_LIMIT_PRIORITY_CLASS|JOB_OBJECT_LIMIT_JOB_TIME;

//設定作業限制

SetInformationJobObject(hJob,JobObjectBasicLimitInformation,&jobli,

sizeof

(jobli));

//為作業添加一些基本UI限制

//基本UI限制結構體

JOBOBJECT_BASIC_UI_RESTRICTIONS jobuir;

//初始無限制

jobuir.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE;

//

//增加限制:作業(程序)不能登出作業系統

jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;

//增加限制:作業(程序)不能通路 系統的使用者對象(如其他視窗)

jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;

//設定作業限制

SetInformationJobObject(hJob,JobObjectBasicUIRestrictions,&jobuir,

sizeof

(jobuir));

//建立程序,并添加到作業中。程序初始化時必須是挂起狀态,保證在添加到作業前不會執行任何代碼

//建立程序

STARTUPINFO si={

sizeof

(si)};

PROCESS_INFORMATION pi;

CreateProcess(_T(

"C:\\Windows\\System32\\cmd.exe"

),NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);

//CREATE_SUSPENDED

//将程序添加到作業

AssignProcessToJobObject(hJob,pi.hProcess);

//喚醒程序(的主線程)

ResumeThread(pi.hThread);

//關閉句柄

CloseHandle(pi.hThread);

//等待程序結束或作業CPU時間耗完

HANDLE

h[2];

h[0] = pi.hProcess;

h[1] = hJob;

DWORD

ret = WaitForMultipleObjects(2,h,FALSE,INFINITE);

switch

(ret-WAIT_OBJECT_0){

case

0:

//程序結束

MessageBox(NULL,_T(

"程序結束"

),_T(

"提示"

),MB_OK);

break

;

case

1:

//作業配置設定的CPU時間耗完

MessageBox(NULL,_T(

"時間耗盡"

),_T(

"提示"

),MB_OK);

break

;

}

//關閉句柄

CloseHandle(pi.hProcess);

CloseHandle(hJob);

return

0;

}

(3) CreateJobObject 建立作業

1 2 3 4

HANDLE

WINAPI CreateJobObject(

//建立作業核心對象

__in_opt  LPSECURITY_ATTRIBUTES lpJobAttributes,

//安全結構體

__in_opt 

LPCTSTR

lpName  

//名稱,可以為NULl

);

(4)作業限制 和 SetInformationJobObject

作業限制類型有:基本限制、擴充限制、UI限制、安全性限制

使用SetInformationJobObject可以為作業指定限制。

1 2 3 4 5 6

BOOL

WINAPI SetInformationJobObject( 

//設定作業限制

__in 

HANDLE

hJob,                           

//要添加限制的作業

__in  JOBOBJECTINFOCLASS JobObjectInfoClass, 

//限制的類型

__in 

LPVOID

lpJobObjectInfo,                

//限制的值

__in 

DWORD

cbJobObjectInfoLength            

//限制的值的長度

);

限制類型 說明 第二個參數的值 第三個參數的結構
基本限制 CPU配置設定限制 JobObjectBasicLimitInformation JOBOBJECT_BASIC_LIMIT_INFORMATION
擴充限制 基本限制+記憶體配置設定限制 JobObjectExtendedLimitInformation JOBOBJECT_EXTENDED_LIMIT_INFORMATION
基本UI限制 防止作業中程序改變UI JobObjectBasicUIRestictions JOBOBJECT_BASIC_UI_RESTRICTIONS
安全性限制 防止作業中程序通路保密資源 JobObjectSecurityLimitInformation JOBOBJECT_SECURITY_LIMIT_INFORMATION

[1] 基本限制

1 2 3 4 5 6 7 8 9 10 11 12

//基本限制:CPU限制

typedef

struct

_JOBOBJECT_BASIC_LIMIT_INFORMATION {

LARGE_INTEGER PerProcessUserTimeLimit;

//如果LimitFlags含有JOB_OBJECT_LIMIT_PROCESS_TIME,則此參數表示配置設定給每個程序的使用者模式執行時間,機關100ns.逾時程序會被終止

LARGE_INTEGER PerJobUserTimeLimit;    

//如果LimitFlags含有JOB_OBJECT_LIMIT_JOB_TIME,則此參數表示配置設定給作業的使用者模式執行時間,逾時作業會被終止

DWORD

LimitFlags;             

//指明哪些限制對作業有效

SIZE_T

MinimumWorkingSetSize;  

//如果LimitFlags含有JOB_OBJECT_LIMIT_WORKINGSET,則此參數表示作業中每個程序的最小工作集大小

SIZE_T

MaximumWorkingSetSize;  

//同上,最大工作集大小

DWORD

ActiveProcessLimit;     

//如果LimitFlags含有JOB_OBJECT_LIMIT_ACTIVE_PROCESS,則此參數表示作業中可以同時運作的最大程序數量

ULONG_PTR

Affinity;               

//如果LimitFlags含有JOB_OBJECT_LIMIT_AFFINITY,則此參數表示能夠運作的程序的CPU子集

DWORD

PriorityClass;          

//如果LimitFlags含有JOB_OBJECT_LIMIT_PRIORITY_CLASS,則此參數表示作業中所有程序的優先級

DWORD

SchedulingClass;        

//如果LimitFlags含有JOB_OBJECT_LIMIT_SCHEDULING_CLASS,則此參數表示相同優先級的作業的排程優先級(0-9,預設5),值越大,CPU時間越長

} JOBOBJECT_BASIC_LIMIT_INFORMATION, *PJOBOBJECT_BASIC_LIMIT_INFORMATION;

[2] 擴充限制

1 2 3 4 5 6 7 8 9

//擴充限制:基本限制+記憶體限制

typedef

struct

_JOBOBJECT_EXTENDED_LIMIT_INFORMATION {

JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;

//基本限制

IO_COUNTERS                       IoInfo;               

//保留不用。IO計數器

SIZE_T

ProcessMemoryLimit;   

//每個程序能使用的記憶體量(“基本限制”參數的LimitFlags需含有JOB_OBJECT_LIMIT_PROCESS_MEMORY)

SIZE_T

JobMemoryLimit;       

//作業(所有程序)能使用的記憶體量(“基本限制”參數的LimitFlags需含有JOB_OBJECT_LIMIT_JOB_MEMORY )

SIZE_T

PeakProcessMemoryUsed;

//隻讀。單個程序需要使用的記憶體最大值

SIZE_T

PeakJobMemoryUsed;    

//隻讀。作業需要使用的記憶體最大值

} JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;

[3] 基本UI限制

1 2 3 4

//基本UI限制

typedef

struct

_JOBOBJECT_BASIC_UI_RESTRICTIONS {

DWORD

UIRestrictionsClass;

//下表标志中一個或是組合

} JOBOBJECT_BASIC_UI_RESTRICTIONS, *PJOBOBJECT_BASIC_UI_RESTRICTIONS;

說明
JOB_OBJECT_UILIMIT_EXITWINDOWS 防止程序通過ExitWindowsEx函數退出、關閉、重新開機或關閉系統電源
JOB_OBJECT_UILIMIT_READCLIPBOARD 防止程序讀取剪切闆的内容
JOB_OBJECT_UILIMIT_WRITECLIPBOARD 防止程序寫剪切闆内容
JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS 防止程序通過SystemParametersInfor函數來改變系統參數
JOB_OBJECT_UILIMIT_DISPLAYSETTINGS 防止程序通過ChangeDisplaySettings函數來改變顯示設定
JOB_OBJECT_UILIMIT_GLOBALATOMS 防止程序通路全局的基本結構表,為作業配置設定自己的基本結構表,作業中程序隻能通路該表。
JOB_OBJECT_UILIMIT_DESKTOP 防止程序使用CreateDesktop或SwitchDesktop函數建立或轉換桌面
JOB_OBJECT_UILIMIT_HANDLES 防止程序使用作業外部的程序建立的使用者對象的句柄(如HWND)

[4] 安全性限制

Windows XP(不包括XP)之後的系統不再支援該限制,需要為每個程序單獨指定安全設定。

(5) AssignProcessToJobObject 将程序添加到作業

要添加到作業的程序在建立時,需使用CREATE_SUSPEND标志,防止加入作業前程序執行任何代碼。

1 2 3

BOOL

WINAPI AssignProcessToJobObject(  __in 

HANDLE

hJob,   

//作業句柄

__in 

HANDLE

hProcess

//程序句柄

);

一個程序加入到一個作業後,不能再轉到另一個作業。

作業中的程序生成的新程序會自動成為作業的一部分。可以通過下面兩種方法改變這種特性:

[1] 打開JOBOBJECT_BASIC_LIMIT_INFROMATION 的LimitFlags成員的JOB_OBJECT_BREAKAWAY_OK标志,告訴系統,新生成的程序可以在作業外部運作。同時使用CREATE_BREAKAWAY_FROM_JOB 标志調用CreateProcess建立新程序

[2] 打開JOBOBJECT_BASIC_LIMIT_INFROMATION 的LimitFlags成員的JOB_OBJECT_SILENT_BREAKAWAY_OK标志,告訴系統,新生成的程序可以在作業外部運作。

(6) 終止作業

1 2 3 4

BOOL

WINAPI TerminateJobObject(

__in 

HANDLE

hJob,   

//作業

__in 

UINT

uExitCode 

//退出碼。作業中所有程序的退出碼自動設為uExitCode

);

(7) QueryInformationJobObject 查詢作業的統計資訊

(8) 作業的通知消息

建立一個IO完成端口(IO Completion Port)核心對象,然後将作業對象或多個作業對象與完成端口關聯起來(使用SetInformationJobObject函數),然後讓一個或多個線程在完成端口上等待作業通知的到來。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

#include <windows.h>

#include <process.h>  //_beginthreadex

#include <tchar.h>

#define CMPKEY_JOBOBJECT 1

#define CMPKEY_TERMINATE 2

typedef

unsigned (__stdcall *PTHREAD_START) (

void

*);

//IO完成端口監聽線程回調函數

DWORD

WINAPI JobNotify(

LPVOID

lpParam)

{

HANDLE

hIOCP = (

HANDLE

)lpParam;

while

(TRUE)

{

DWORD

dwBytesTransferred;

ULONG_PTR

CompKey;

LPOVERLAPPED po;

//從IO完成端口中擷取一個消息

GetQueuedCompletionStatus(hIOCP,&dwBytesTransferred,&CompKey,&po,INFINITE);

//退出消息

if

(CompKey == CMPKEY_TERMINATE)

{

MessageBox(NULL,_T(

"監聽線程退出"

),_T(

"提示"

),MB_OK);

break

;

}

//來自作業對象hJob的消息

if

(CompKey == CMPKEY_JOBOBJECT)

{

MessageBox(NULL,_T(

"收到來自作業的消息"

),_T(

"提示"

),MB_OK);

switch

(dwBytesTransferred){

case

JOB_OBJECT_MSG_END_OF_JOB_TIME:

MessageBox(NULL,_T(

"作業限制時間耗盡"

),_T(

"提示"

),MB_OK);

break

;

case

JOB_OBJECT_MSG_END_OF_PROCESS_TIME:

{

TCHAR

szProcessName[MAX_PATH];

HANDLE

hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,(

DWORD

)po);

if

(hProcess == NULL){

_stprintf(szProcessName,_T(

"%s"

),_T(

"未知程序名"

));

}

else

{

DWORD

dwSize = (

DWORD

)MAX_PATH;

QueryFullProcessImageName(hProcess,0,szProcessName,&dwSize);

CloseHandle(hProcess);

}

TCHAR

info[MAX_PATH];

_stprintf(info,_T(

"程序%s(ID=%d)限制時間耗盡 "

),szProcessName,po);

MessageBox(NULL,info,_T(

"提示"

),MB_OK);

}

break

;

case

JOB_OBJECT_MSG_ACTIVE_PROCESS_LIMIT:

MessageBox(NULL,_T(

"運作的程序超過限制"

),_T(

"提示"

),MB_OK);

break

;

case

JOB_OBJECT_MSG_NEW_PROCESS:

MessageBox(NULL,_T(

"作業中産生新程序"

),_T(

"提示"

),MB_OK);

break

;

case

JOB_OBJECT_MSG_EXIT_PROCESS:  {

TCHAR

szProcessName[MAX_PATH];

HANDLE

hProcess = OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,FALSE,(

DWORD

)po);

if

(hProcess == NULL){

_stprintf(szProcessName,_T(

"%s"

),_T(

"未知程序名"

));

}

else

{

DWORD

dwSize = (

DWORD

)MAX_PATH;

QueryFullProcessImageName(hProcess,0,szProcessName,&dwSize);

CloseHandle(hProcess);

}

TCHAR

info[MAX_PATH];

_stprintf(info,_T(

"程序%s(ID=%d)終止 "

),szProcessName,po);

MessageBox(NULL,info,_T(

"提示"

),MB_OK);

}

break

;

}

}

}

return

0;

}

int

WINAPI wWinMain(

HINSTANCE

hInstance,

HINSTANCE

,

PWSTR

lpCmdLine,

int

nShowCmd){

//建立一個作業核心對象

HANDLE

hJob = CreateJobObject(NULL,NULL);

//

//建立一個IO完成端口

HANDLE

hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);

//建立一個線程監聽IO完成端口通知消息

HANDLE

hThreadIOCP = (

HANDLE

)_beginthreadex(NULL,0,(PTHREAD_START)JobNotify,(

LPVOID

)hIOCP,0,NULL);

//将IO完成端口與作業關聯

JOBOBJECT_ASSOCIATE_COMPLETION_PORT jobacp;

jobacp.CompletionKey = (

PVOID

)CMPKEY_JOBOBJECT; 

//任意一個全局唯一的值

jobacp.CompletionPort = hIOCP;                  

//IO完成端口句柄

SetInformationJobObject(hJob,JobObjectAssociateCompletionPortInformation,&jobacp,

sizeof

(jobacp));

//關聯

//建立程序,并添加到作業中。程序初始化時必須是挂起狀态,保證在添加到作業前不會執行任何代碼

STARTUPINFO si={

sizeof

(si)};

PROCESS_INFORMATION pi;

CreateProcess(_T(

"C:\\Windows\\System32\\cmd.exe"

),NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&si,&pi);

//CREATE_SUSPENDED

AssignProcessToJobObject(hJob,pi.hProcess);

//将程序添加到作業

MessageBox(NULL,_T(

"111"

),_T(

"Tips"

),MB_OK);

ResumeThread(pi.hThread);

//喚醒程序(的主線程)

CloseHandle(pi.hThread);

//關閉句柄

CloseHandle(pi.hProcess);

MessageBox(NULL,_T(

"MESSAGE"

),_T(

"Tips"

),MB_OK);

//發送一條消息給IO完成端口,結束IO完成端口線程

PostQueuedCompletionStatus(hIOCP,0,CMPKEY_TERMINATE,NULL);

//等待IO完成端口線程終止

WaitForSingleObject(hThreadIOCP,INFINITE);

//關閉句柄

CloseHandle(hIOCP);

CloseHandle(hThreadIOCP);

CloseHandle(hJob);

return

0;

}

17. 線程

(1) 線程

線程由兩部分組成

-> 線程的核心對象。作業系統用來管理線程的資料結構,也是用來存放線程統計資訊的結構

-> 線程堆棧,用于維護線程在執行代碼時需要的所有函數參數和局部變量。

程序是不活潑的。程序從來不執行任何東西,它隻是線程的容器。

(2) 線程回調函數(入口函數)

1 2 3 4 5 6

DWORD

WINAPI ThreadProc(

LPVOID

lpParam)

{

DWORD

dwResult = 0;

//...

return

dwResult;

}

(3) CreateThread 和_beginthreadex 差別

-> CreateThread是Windows API函數,_beginthreadex是C\C++ Runtime Libarary 函數。

-> _beginthreadex内部調用了CreateThread. [源碼在thread.c中]

[了解]

C\C++運作庫中一些函數使用了全局變量(如errno),直接使用CreateThread會出現多線程同步時的不安全,_beginthreadex則為這些全局變量做了處理,放到了一個_tiddata結構體中,并傳遞給了線程,使得每個線程了都有一份獨立的“全局變量”(TLS,Thread Local Storage)。

[如果使用C\C++ Runtime Libaray函數(更準确的說是使用了_tiddata結構體的函數),則應該使用_beginthreadex,防止記憶體洩露和多線程不安全問題] 具體的情況有:

-> 使用了malloc和free,或者new和delete

-> 使用了stdio.h或io.h中的函數

-> 使用了浮點變量或浮點運算函數

-> 調用了任何一個使用了靜态緩沖區的Runtime函數,如asctime(),strtok()或rand()

(4) CreateThread 建立線程

1 2 3 4 5 6 7 8

HANDLE

WINAPI CreateThread(

__in_opt   LPSECURITY_ATTRIBUTES lpThreadAttributes,

//NULL,或者将其的bInheritHandle成員置為TRUE後傳入,使該線程核心對象句柄可被子程序繼承。

__in      

SIZE_T

dwStackSize,                      

//線程的堆棧大小(機關位元組)。0表示使用預設的堆棧大小

__in       LPTHREAD_START_ROUTINE lpStartAddress,   

//線程回調函數

__in_opt  

LPVOID

lpParameter,                      

//傳遞給回調函數的參數

__in      

DWORD

dwCreationFlags,                   

//标志。0,建立後立即運作;CREATE_SUSPENDED,挂起,調用ResumeThread時再運作;

__out_opt 

LPDWORD

lpThreadId                       

//線程ID

);

-> dwStackSize 參數,設定線程的堆棧大小,0表示使用系統預設的大小

在Visual Studio 2010中,預設的線程堆棧大小是 1MB,由連結器的/Stack:reserve 開關控制。可以在Liker ->System ->Stack Reserve Size中修改預設的堆棧大小,機關是位元組B。

Windows 核心程式設計 學習筆記 (第三部分)

(5) 線程的終止

-> 線程回調函數的傳回 (推薦): 能正确釋放所有C++對象,正确釋放線程堆棧,設定線程退出碼,遞減線程核心對象的計數

-> ExitThread : 不能釋放C++對象。

-> TerminateThread:異步函數,函數傳回時線程不一定終止了,可使用WaitForSingleObject來等待終止。父程序終止時才會撤銷線程的堆棧。

(6) 線程建立和初始化的細節

Windows 核心程式設計 學習筆記 (第三部分)

BaseThreadStart是一個為文檔化的函數,它首先建立一個結構化異常處理幀(SHE,使線程産生的異常能得到系統預設處理),然後調用線程的回調函數,線程傳回時,調用ExitThread。

(7) _beginthreadex 函數

1 2 3 4 5 6 7

uintptr_t

_beginthreadex(

// NATIVE CODE

void

*security,

unsigned stack_size,

unsigned ( __stdcall *start_address )(

void

* ),

void

*arglist,

unsigned initflag,

unsigned *thrdaddr  );

_beginthreadex 隻存在于C\C++ Runtime Libarary 的多線程版本中。_beginthreadex的參數類型不依賴Windows API,參數功能與CreateThread大緻相同,可以通過宏實作轉換

1 2 3 4 5 6 7 8 9 10 11

typedef

unsigned (__stdcall *PTHREAD_START) (

void

*);

#define chBEGINTHREADEX(psa, cbStackSize, pfnStartAddr, \

pvParam, dwCreateFlags, pdwThreadId)                 \

((

HANDLE

)_beginthreadex(                          \

(

void

*)        (psa),                         \

(unsigned)      (cbStackSize),                 \

(PTHREAD_START) (pfnStartAddr),                \

(

void

*)        (pvParam),                     \

(unsigned)      (dwCreateFlags),               \

(unsigned *)    (pdwThreadId)))

_beginthreadex 内部首先建立一個tiddata結構體,然後将回調函數的位址(函數名)和參數儲存到tiddata結構體,然後調用CreateThead函數,CreateThread函數中回調函數為_threadstartex,傳給_threadstartex的參數為tiddata結構體。

18. 線程排程

每個線程都擁有一個上下文結構體(CONTEXT),該結構體儲存線上程的核心對象中,該結構體儲存了線程上次運作時該線程的CPU寄存器的狀态。每隔20ms左右,Windows檢視目前存在的所有線程核心對象,CPU選擇一個可排程的核心對象(不需要排程的線程如:暫停計數器 >= 1,即處于暫停狀态的線程;等待事件發生的線程),将它加載到CPU寄存器中,這個操作稱為上下文切換(Context Swiche).

(1) 線程挂起和恢複

一個線程可以挂起若幹次(最大為MAXIMUM_SUSPEND_COUNT),如線程被挂起3次,則必須恢複3次,它才可以被配置設定CPU。使用CREATE_SUSPEND标志建立一個挂起狀态的線程或者DWORD SuspendThread(HANDLE hThread) 可以挂起一個線程。ResumeThread可以恢複一次。

(2) Sleep 函數

1

VOID

WINAPI Sleep(  __in 

DWORD

dwMilliseconds );

-> 系統保證在dwMilliseconds 時間内不排程線程,但過後不保證馬上喚醒

-> dwMilliseconds = INFINITE,永不排程線程。最好不要這樣做

-> dwMilliseconds = 0, 線程放棄剩餘的時間片,迫使系統排程另一個線程

(3) 切換線程

1

BOOL

WINAPI SwitchToThread(

void

);

讓作業系統排程另一個線程,如果發生切換,傳回非0值,沒有切換,傳回0。

(4) 線程執行時間

1 2 3 4 5 6 7

BOOL

WINAPI GetThreadTimes(

__in  

HANDLE

hThread,            

//線程句柄

__out  LPFILETIME lpCreationTime, 

//建立時間

__out  LPFILETIME lpExitTime,     

//退出時間。如果線程仍在運作,則為未定義

__out  LPFILETIME lpKernelTime,   

//核心時間(機關100ns)

__out  LPFILETIME lpUserTime      

//使用者時間 (機關100ns)

);

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52

#include <windows.h>

#include <process.h>  //_beginthreadex

#include <tchar.h>

DWORD

WINAPI ThreadFun(

LPVOID

lpParam)

{

DWORD

dwResult = 0;

return

dwResult;

}

//FILETIME 抓換為 __int64

__int64

FileTimeToQuadWord(PFILETIME pft) {

return

(Int64ShllMod32(pft->dwHighDateTime, 32) | pft->dwLowDateTime);

}

int

WINAPI wWinMain(

HINSTANCE

hInstance,

HINSTANCE

,

PWSTR

lpCmdLine,

int

nShowCmd){

HANDLE

hThread = CreateThread(NULL,0,ThreadFun,NULL,0,NULL);

FILETIME ftCreate;

FILETIME ftExit;

FILETIME ftKernel;

FILETIME ftUser;

//擷取線程的FILETIME時間

BOOL

ret = GetThreadTimes(hThread,&ftCreate,&ftExit,&ftKernel,&ftUser);

if

(!ret)

MessageBox(NULL,_T(

"擷取時間失敗"

),_T(

"提示"

),MB_OK);

//建立時間 - 轉化為本地時間

SYSTEMTIME stUTCCreate,stLocalCreate;

FileTimeToSystemTime(&ftCreate,&stUTCCreate);                    

//FILETIME轉換為UTC時間

SystemTimeToTzSpecificLocalTime(NULL,&stUTCCreate,&stLocalCreate);

//UTC時間轉換為本地時間   

TCHAR

msg_create[256];

_stprintf(msg_create,_T(

"建立時間:%d-%d-%d %d:%d:%d %d"

),stLocalCreate.wYear,stLocalCreate.wMonth,stLocalCreate.wDay,

stLocalCreate.wHour,stLocalCreate.wMinute,stLocalCreate.wSecond,stLocalCreate.wMilliseconds);

MessageBox(NULL,msg_create,_T(

"時間"

),MB_OK);

WaitForSingleObject(hThread,INFINITE); 

//等待線程結束,再統計核心時間

//核心時間 - 轉化為具體數值   

__int64

kernelTime = FileTimeToQuadWord(&ftKernel);

TCHAR

msg_kernel[256];

_stprintf(msg_kernel,_T(

"核心時間:%I64d"

),kernelTime);

MessageBox(NULL,msg_kernel,_T(

"時間"

),MB_OK);

CloseHandle(hThread);

return

0;

}

(5) CONTEXT 上下文

CONTEXT結構體包含了特定處理器的寄存器資料。系統使用CONTEXT結構執行各種内部操作。目前已經存在為Intel、MIPS、Alpha、PowerPC處理器定義的CONTEXT結構。具體定義在WinNT.h 中。

(6) Windows 線程排程

每個線程都會被賦予一個從0(最低)到31(最高)的優先級。

系統引導時,會建立一個特殊的線程,稱為0頁線程,優先級為0,它是整個系統中唯一一個優先級為0的線程。當系統沒有任何線程需要執行時,0頁線程負責将系統中的所有空閑RAM頁面置0

-> Microsoft 沒有将排程程式的行為特性完全固定下來

-> Microsoft 沒有讓應用程式充分利用排程程式的特性

-> Microsoft 聲稱排程程式的算法是變化的,在編寫代碼時應有所準備。

(7) 程序優先級

Windows 中定義了如下6個優先級類

優先級 辨別符
實時 REALTIME_PRIORITY_CLASS
HIGH_PRIORITY_CLASS
高于正常 ABOVE_NORMAL_PRIORITY_CLASS
正常 NORMAL_PRIORITY_CLASS
低于正常 BELOW_NORMAL_PRIORITY_CLASS
空閑 IDLE_PRIORITY_CLASS

->CreateProcess時,通過辨別位參數設定,辨別位參數取值為上表中一個。

-> 通過SetPriorityClass函數設定優先級。

1

SetPriorityClass(GetCurrentProcess(),IDLE_PRIORITY_CLASS);

通過GetPriorityClass函數擷取優先級

1

DWORD

GetPriorityClass(

HANDLE

hProcess);

(8) 線程的相對優先級

Windows 核心程式設計 學習筆記 (第三部分)

線程的相對優先級有7個等級:關鍵時間、最高、高于正常、正常、低于正常、最低、空閑。

程序的優先級類和線程相對優先級的映射

線程相對優先級 空閑 低于正常 正常 高于正常 實時 線程相對優先級辨別符
關鍵時間 15 15 15 15 15 31 THREAD_PRIORITY_TIME_CRITICAL
最高 6 8 10 12 15 26 THREAD_PRIORITY_HIGHEST
高于正常 5 7 9 11 14 25 THREAD_PRIORITY_ABOVE_NORMAL
正常 4 6 8 10 13 24 THREAD_PRIORITY_NORMAL
低于正常 3 5 7 9 12 23 THREAD_PRIORITY_BELOW_NORMAL
最低 2 4 6 8 11 22 THREAD_PRIORITY_LOWEST
空閑 1 1 1 1 1 16 THREAD_PRIORITY_IDLE

通過SetThreadPriority函數設定線程的優先級.

1 2 3 4

BOOL

WINAPI SetThreadPriority(

__in 

HANDLE

hThread,

__in 

int

nPriority  

//線程相對優先級辨別符

);

GetThreadPriority 函數傳回線程的相對優先級。

(9) 動态提高線程的優先級

綜合考慮線程的相對優先級和線程的程序優先級,系統就可以确定線程的優先級等級,稱為線程的基本優先級等級。

系統常常要提高線程的優先級等級,以便對視窗消息或讀取磁盤等IO事件作出響應。

系統隻能為基本優先級等級在1~15之間的線程提高其優先級等級(稱為動态優先級範圍),但等級絕不會提高到實時範圍(等級高于15)。實時範圍的線程能夠執行大多數作業系統的函數,是以給等級的提高規定一個範圍,就可以防止應用程式幹擾作業系統的運作。

通過SetProcessPriorityBoost可以啟用或停用系統自動臨時提升線程優先級的功能。

1 2 3

BOOL

WINAPI SetProcessPriorityBoost( 

//停用或啟用系統自動對程序中線程優先級的臨時提升

__in 

HANDLE

hProcess,            __in 

BOOL

DisablePriorityBoost 

//TRUE 停用,FALSE 啟用

);

通過GetProcessPriorityBoost可以擷取目前的啟用或停用狀态。

(10) 親緣性

-> 軟親緣性: 按照預設設定,系統将線程配置設定給CPU時,如果其他因素相同,它會優先在上次運作的CPU上運作線程。[讓線程留在單個CPU上,有助于重複使用仍在CPU cache中的資料]

-> 硬親緣性: 直接控制線程在某個CPU上運作。

[1] SetProcessAffinityMask 設定程序的CPU親緣性

1 2 3 4

BOOL

WINAPI SetProcessAffinityMask(

//設定程序所有線程的CPU親緣性

__in 

HANDLE

hProcess,

__in 

DWORD_PTR

dwProcessAffinityMask

//親緣性掩碼。位屏蔽,指明可以在哪些CPU上運作。

);

如 dwProcessAffinityMask = 0x00000005,則表明隻可以在CPU0 和 CPU1上運作(5->101).

通過GetProcessAffinityMask函數可以擷取程序的親緣性掩碼。

[2] SetThreadAffinityMask 設定線程的親緣性

1 2 3 4

DWORD_PTR

WINAPI SetThreadAffinityMask(

//設定線程的CPU親緣性

__in 

HANDLE

hThread,

__in 

DWORD_PTR

dwThreadAffinityMask

//親緣性掩碼

);

強制給線程配置設定一個CPU的做法,有時不妥當。可以通過SetThreadIdealProcessor函數為線程選擇一個理想的CPU

1 2 3 4

DWORD

WINAPI SetThreadIdealProcessor( 

//設定線程的首選CPU

__in 

HANDLE

hThread,

__in 

DWORD

dwIdealProcessor      

//CPU的編号。如0表示CPU0,1表示CPU1。最大為MAXIMUM_PROCESSORS (32)

);

作者:JarvisChu

原文連結:Windows 核心程式設計 學習筆記 (第三部分)

版權聲明:自由轉載-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0

标簽: windows程式設計, 作業, 筆記, 線程排程

繼續閱讀