目錄
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 | |
(3) CreateJobObject 建立作業
1 2 3 4 | |
(4)作業限制 和 SetInformationJobObject
作業限制類型有:基本限制、擴充限制、UI限制、安全性限制
使用SetInformationJobObject可以為作業指定限制。
1 2 3 4 5 6 | |
限制類型 | 說明 | 第二個參數的值 | 第三個參數的結構 |
基本限制 | 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 | |
[2] 擴充限制
1 2 3 4 5 6 7 8 9 | |
[3] 基本UI限制
1 2 3 4 | |
值 | 說明 |
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 | |
一個程序加入到一個作業後,不能再轉到另一個作業。
作業中的程序生成的新程序會自動成為作業的一部分。可以通過下面兩種方法改變這種特性:
[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 | |
(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 | |
17. 線程
(1) 線程
線程由兩部分組成
-> 線程的核心對象。作業系統用來管理線程的資料結構,也是用來存放線程統計資訊的結構
-> 線程堆棧,用于維護線程在執行代碼時需要的所有函數參數和局部變量。
程序是不活潑的。程序從來不執行任何東西,它隻是線程的容器。
(2) 線程回調函數(入口函數)
1 2 3 4 5 6 | |
(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 | |
-> dwStackSize 參數,設定線程的堆棧大小,0表示使用系統預設的大小
在Visual Studio 2010中,預設的線程堆棧大小是 1MB,由連結器的/Stack:reserve 開關控制。可以在Liker ->System ->Stack Reserve Size中修改預設的堆棧大小,機關是位元組B。
(5) 線程的終止
-> 線程回調函數的傳回 (推薦): 能正确釋放所有C++對象,正确釋放線程堆棧,設定線程退出碼,遞減線程核心對象的計數
-> ExitThread : 不能釋放C++對象。
-> TerminateThread:異步函數,函數傳回時線程不一定終止了,可使用WaitForSingleObject來等待終止。父程序終止時才會撤銷線程的堆棧。
(6) 線程建立和初始化的細節
BaseThreadStart是一個為文檔化的函數,它首先建立一個結構化異常處理幀(SHE,使線程産生的異常能得到系統預設處理),然後調用線程的回調函數,線程傳回時,調用ExitThread。
(7) _beginthreadex 函數
1 2 3 4 5 6 7 | |
_beginthreadex 隻存在于C\C++ Runtime Libarary 的多線程版本中。_beginthreadex的參數類型不依賴Windows API,參數功能與CreateThread大緻相同,可以通過宏實作轉換
1 2 3 4 5 6 7 8 9 10 11 | |
_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 | |
-> 系統保證在dwMilliseconds 時間内不排程線程,但過後不保證馬上喚醒
-> dwMilliseconds = INFINITE,永不排程線程。最好不要這樣做
-> dwMilliseconds = 0, 線程放棄剩餘的時間片,迫使系統排程另一個線程
(3) 切換線程
1 | |
讓作業系統排程另一個線程,如果發生切換,傳回非0值,沒有切換,傳回0。
(4) 線程執行時間
1 2 3 4 5 6 7 | |
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 | |
(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 | |
通過GetPriorityClass函數擷取優先級
1 | |
(8) 線程的相對優先級
線程的相對優先級有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 | |
GetThreadPriority 函數傳回線程的相對優先級。
(9) 動态提高線程的優先級
綜合考慮線程的相對優先級和線程的程序優先級,系統就可以确定線程的優先級等級,稱為線程的基本優先級等級。
系統常常要提高線程的優先級等級,以便對視窗消息或讀取磁盤等IO事件作出響應。
系統隻能為基本優先級等級在1~15之間的線程提高其優先級等級(稱為動态優先級範圍),但等級絕不會提高到實時範圍(等級高于15)。實時範圍的線程能夠執行大多數作業系統的函數,是以給等級的提高規定一個範圍,就可以防止應用程式幹擾作業系統的運作。
通過SetProcessPriorityBoost可以啟用或停用系統自動臨時提升線程優先級的功能。
1 2 3 | |
通過GetProcessPriorityBoost可以擷取目前的啟用或停用狀态。
(10) 親緣性
-> 軟親緣性: 按照預設設定,系統将線程配置設定給CPU時,如果其他因素相同,它會優先在上次運作的CPU上運作線程。[讓線程留在單個CPU上,有助于重複使用仍在CPU cache中的資料]
-> 硬親緣性: 直接控制線程在某個CPU上運作。
[1] SetProcessAffinityMask 設定程序的CPU親緣性
1 2 3 4 | |
如 dwProcessAffinityMask = 0x00000005,則表明隻可以在CPU0 和 CPU1上運作(5->101).
通過GetProcessAffinityMask函數可以擷取程序的親緣性掩碼。
[2] SetThreadAffinityMask 設定線程的親緣性
1 2 3 4 | |
強制給線程配置設定一個CPU的做法,有時不妥當。可以通過SetThreadIdealProcessor函數為線程選擇一個理想的CPU
1 2 3 4 | |
作者:JarvisChu
原文連結:Windows 核心程式設計 學習筆記 (第三部分)
版權聲明:自由轉載-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0
标簽: windows程式設計, 作業, 筆記, 線程排程