7.7 在实际上下文中谈CONTEXT结构
(1)线程CONTEXT记录线程的状态(如CPU各寄存器状态),以供下次调度时从停止处继续。
(2)CONTEXT的结构(要获得或设置时,必须在Context.ContextFlags设置相应的标志)
标志 |
说明 |
CONTEXT_CONTROL |
控制寄存器,如EIP、ESP,EBP等 |
CONTEXT_INTEGER |
整数寄存器,如EDI、ESI、EBX、EDX、ECX、EAX等 |
CONTEXT_FLOATING_POINT |
浮点寄存器,将寄存器结果返回到FLOATING_SAVE_AREA FloagSave |
CONTEXT_SEGMENTS |
段寄存器,如GS、FS、ES、DS |
CONTEXT_DEBUG_REGISTERS |
调试寄存器,如DR0、……、DR7 |
CONTEXT_EXTENDED_REGISTERS |
扩展寄存器,将寄存器的结果返回到 BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]数组中 |
★CONTEXT_FULL标志 = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS
(3)获取和设置上下文
①先挂起线程和设置CONTEXT结构体相应的标志
②GetSetThreadContext;
【ThreadContext程序】显示线程上下文的CPU寄存器状态
#include <windows.h> #include <tchar.h> #include <locale.h> //线程函数 DWORD WINAPI ThreadProc(PVOID pvParam) { HANDLE hEvent = (HANDLE)pvParam; WaitForSingleObject(hEvent, INFINITE); CloseHandle(hEvent); return 0; } int _tmain() { _tsetlocale(LC_ALL, _T("chs")); HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); HANDLE hEventDup = NULL; DuplicateHandle(GetCurrentProcess(), hEvent, GetCurrentProcess(), &hEventDup, DUPLICATE_SAME_ACCESS,FALSE,0); HANDLE hThread = CreateThread(NULL, 0, ThreadProc, hEventDup, CREATE_SUSPENDED,NULL); ResumeThread(hThread); SuspendThread(hThread); CONTEXT ct = {0}; ct.ContextFlags = CONTEXT_ALL; GetThreadContext(hThread, &ct); //显示CONTEXT的内容 _tprintf(_T("CPU寄存器状态: ")); _tprintf(_T(" EAX=0x%08X,EBX=0x%08X "),ct.Eax,ct.Ebx); _tprintf(_T(" ECX=0x%08X,EDX=0x%08X "), ct.Ecx, ct.Edx); _tprintf(_T(" ESI=0x%08X,EDI=0x%08X "), ct.Esi, ct.Edi); _tprintf(_T(" EIP=0x%08X,ESP=0x%08X "), ct.Eip, ct.Esp); _tprintf(_T(" EBP=0x%08X,EFL=0x%08X "), ct.Ebp, ct.EFlags); ResumeThread(hThread); SetEvent(hEvent); CloseHandle(hEvent); CloseHandle(hThread); _tsystem(_T("PAUSE")); return 0; }
7.8 线程的优先级
(1)线程优先级被分为0-31级,其中0级最低、31级最高。但编程时不能直接更改这个数字,要通过“进程优先级类”和“线程相对优先级”设置。
(2)每次调度时,如果有优先级31级线程可供调度,那系统将CPU分配给该线程。结束后,会查看是否还在在优先级31级的线程可调度,如果存在,它将获得CPU,其他的0-30级是不会分配CPU。这种现象称为“饥饿”
(3)较高优先高级的线程总是抢占较低优先级的,无论较低优先级的线程是否正在执行,如果正在运行,则会被立即暂停,将将CPU分配给较高优先级线程,而且该线程将获得一个完整的时间片
(4)优先级0的线程是个特殊线程,整个系统中只有一个0等级的线程,称为“页面清零线程”,这个线程负责在没有其他线程需要执行的时候,将系统内存中所有闲置页面清零。
7.9 从抽象角度看优先级
(1)进程优先级类
优先级(及标识符) |
描述 |
Time-critical (REALTIME_PRIORITY_CLASS) |
此进程中的线程必须立即响应,执行实时任务。此进程中的线程学会抢占操作系统的组件的CPU时间,使用该优先级类需极为小心。默认下用户进程是不能运行在该优先级类的,除非用户有InCreateSchedulingPriority(提高计划优先级)的特权。 |
High(高) (HIGH_PRIORITY_CLASS) |
此进程中的线程必须立即响应,执行实时任务。任务管理器运行在这一级。因此用户可通过它来结束失控的进程 |
Above normal(高于标准) (ABOVE_NORMAL_PRIOORITY_CLASS) |
此进程中的线程运行在Normal和Hight优先级类之间 |
Normal(标准) (NORMAL_PRIORITY_CLASS) |
此进程中的线程无需特殊调度 |
Below normal(低于标准) (BELOW_NORMAL_PRIORITY_CLASS) |
此进程中的线程运行在Normal和Idle类之间 |
Idle(空闲) (IDLE_PRIORITY_CLASS) |
在系统空闲时运行。如屏幕保护程序、后台实用程序和统计数据收集软件通常使用该优先级类 |
(2)线程的相对优先级
优先级 |
描述 |
Time0(实时) |
对于进程是real-time优先级类,线程运行在31上;所有其他基本优先级类的进程时运行在15 |
Highest |
线程运行在高于normal之上两个级别 |
Above normal(高于标准) |
线程运行在高于normal之上一个级别 |
Normal(标准) |
运行在normal级别 |
Below normal(低于标准) |
线程运行在低于normal一个级别 |
Lowest |
线程运行在低于normal两个级别 |
Idle(空闲) |
对于real-time优先级类时,线程运行在16,其他优先级类运行在1 |
(3)线程优先级=进程优先级类和线程的相对优先级映射到0-31级的某个优先级上
线程相对优先级 (及标识符) |
进程优先级类 |
|||||
Idle |
Below normal |
Normal |
Above normal |
high |
Real-time |
|
Time-critial (THREAD_PRIORITY_TIME_CRITICAL) |
15 |
15 |
15 |
15 |
15 |
31 |
Highest (THREAD_PRIORITY_HIGHEST) |
6 |
8 |
10 |
12 |
15 |
26 |
Above normal (THREAD_PRIORITY_ABOVE_NORMAL) |
5 |
7 |
9 |
11 |
14 |
25 |
Normal (THREAD_PRIORITY_NORMAL) |
4 |
6 |
8 |
10 |
13 |
24 |
Below normal (THREAD_PRIORITY_BELOW_NORMAL) |
3 |
5 |
7 |
9 |
12 |
23 |
Lowest (THREAD_PRIORITY_LOWEST) |
2 |
4 |
6 |
8 |
11 |
22 |
Idle (THREAD_PRIORITY_IDLE) |
1 |
1 |
1 |
1 |
1 |
16 |
说明:
①大多数程序线程的优先级为8,即进程优先级类为Normal、线程相对优先级为Normal
②线程优先级是相对于进程优先极的,即改变了进程优先级,级程优先权一般也将变化。
③注意:表中线程优先级值没有0,因为0优先级保留给页面清零线程
④进程为real-time优先级类中的线程,共优先级不低于16。同理,非real-time的线程优先级值不高能高于15。
7.10 优先级编程
(1)获取和设置进程优先级类
①在CreateProcess中的fdwCreate参数中传入“进程优先级类”中相应的标志
②调用SetPriorityClass函数设置——(可能需要足够的访问权限,因为这函数可以改变系统中任何进程的优先级)
③GetPriorityClass获取进程优先级类
④可通过命令行(如C:Start /low calc.exe以“低优先级”)来运行就用程序或通过“任务管理器”来改变进程的优先级类
(2)获取和改变线程相对优先级
①在CreateThread中传为CREATE_SUSPENDED,然后调用SetThreadPriority。
②运行中的线程也可通过SetThreadPriority设置相对优先级,将返回前一个优先级。
③GetThreadPriority获取线程相对优先级,但Windows并没有返回线程绝对优先级(即0-31级)的函数。
(3)动态提升线程优先级:
①线程优先级的分类:16~31为实时类型,1~15为动态类型,0为系统类型
②线程的基本优先级=线程相对优先级与进程优先级类映射出来的值(介于0-31)
③有时为及时响应某种事件,系统会动态提升线程优先级,如原来的基本优先级为13,会临时提升级别(如提升2),也就是线程当前优先级达到了15。也可能是某个低优先级的线程长时间处于饥饿状态(如3~4秒),系统会临时将优先级提到15。过两个时间片后,优先级将恢复到原来的基本优先级。(但注意,线程当前优先级不会低于基本优先级)
④系统只提升动类型(即优先级值为1~15)的线程,这个范围被称为动态优先级范围
⑤可通过SetProcessPriorityBoost或SetThreadPriorityBoost来允许或禁止系统动态提升优先级的做法。当然也可以用GetProcessThreadPriorityBoost来查看是否启动这种行为。
(4)为前台进程微调调度程序
①前台进程:即用户正在使用进程的某个窗口,该进程为前台进程。其余为后台进程。
②系统为前台进程的线程微调调度算法,即比后台进程分配更多的时间片。这种微调只在前台进程是Normal优先级类时才进程,其他优先级类时不进行微调。
③可在Windows的“系统属性”对话框→“高级”→“性能”中单击“设置”,在弹出的“性能选项”对话框的“高级”选项卡中的“调整以优先性能中”选择“程序”,如果选择“后台服务”性能,则不会微调。
(5)调度I/O请求优先级
①I/O请求会使得CPU将时间片分配给这种I/O处理,而当一个低优先级的线程获得CPU时,它可以在短时间内产生成千个I/O请求。从而抢得CPU,使高优先级的线程被挂起。
②在WindowVista开始,线程也可以对I/O请求设置优先级。
③通过将线程相对优先极传入THREAD_MODE_BACKGROUND_BEGIN让线程进入后台工作模式此时将只允许发送低优先级的I/O请求,但这同时也降低了线程的CPU调度优先级。要结束后台工作模式里,可以现传入THREAD_MODE_BACKGROUND_END。(但注意,使用这两个标志时,只能传入主调线程的句柄,即GetCurrentThread()。系统不允许改变另一个线程的I/O优先级。
④要改变所有线程I/O请求优先级,可用SetPriorityClass并传入PROCESS_MODE_BACKGROUND_BEGIN和PROCESS_MODE_BACKGROUND_END标志。类似地,系统不允许改变另一个进程中线程的I/O优先级。
⑤为了避免优先级逆转,如果某Normal优先级线程频繁请求I/O操作时,后台优先级线程可以在获得I/O请求前延迟几秒。如果低优先级的线程己经获得了Normal线程要等待的锁,Normal线程可以主动结束等待,但这影响了Normal线程任务的执行。为了防止这类事情发生,甚至应让后台线程不提交I/O请求,以便Normal线程正常执行,但这几乎很不现实。所以应尽量少在Normal线程与台线程之间同步锁。
【Scheduling Lab示例程序】
/************************************************************************/ /* Module:SchdLab.cpp */ /* Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre */ /************************************************************************/ #include "..\..\CommonFiles\CmnHdr.h" #include <windows.h> #include <tchar.h> #include <strsafe.h> #include "resource.h" ////////////////////////////////////////////////////////////////////////// DWORD WINAPI ThreadFunc(PVOID pvParam) { HANDLE hThreadPrimary = (HANDLE)pvParam; //挂起主线程 SuspendThread(hThreadPrimary); chMB( "主线程被挂起,将不再响应输入和产生输出. " "按“确定”按钮恢复主线程,并退出子线程。 " ); ResumeThread(hThreadPrimary); CloseHandle(hThreadPrimary); //启用“挂机按钮” EnableWindow( GetDlgItem(FindWindow(NULL, TEXT("Scheduling Lab")), IDC_SUSPEND), TRUE); return 0; } ////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_SCHEDLAB); //初始化进程优先级类组合框 HWND hWndCtrl = GetDlgItem(hwnd, IDC_PROCESSPRIORITYCLASS); int n = ComboBox_AddString(hWndCtrl, TEXT("高")); ComboBox_SetItemData(hWndCtrl, n, HIGH_PRIORITY_CLASS); //保存当前进程优先级类 DWORD dwpc = GetPriorityClass(GetCurrentProcess()); //判断系统是否支持“低于标准”优先级类 if (SetPriorityClass(GetCurrentProcess(),BELOW_NORMAL_PRIORITY_CLASS)){ //恢复原始的进程优先级类 SetPriorityClass(GetCurrentProcess(), dwpc); //增加“高于标准”优先级类 n = ComboBox_AddString(hWndCtrl, TEXT("高于标准")); ComboBox_SetItemData(hWndCtrl, n, ABOVE_NORMAL_PRIORITY_CLASS); dwpc = 0; //作为系统是否支持“低于标准”优先级类的标志 } //标准优先级类 int nNormal= n = ComboBox_AddString(hWndCtrl, TEXT("标准")); ComboBox_SetItemData(hWndCtrl, n, NORMAL_PRIORITY_CLASS); //“低于标准”优先级类 if (dwpc == 0){ n = ComboBox_AddString(hWndCtrl, TEXT("低于标准")); ComboBox_SetItemData(hWndCtrl, n, BELOW_NORMAL_PRIORITY_CLASS); } n = ComboBox_AddString(hWndCtrl, TEXT("空闲")); ComboBox_SetItemData(hWndCtrl, n, IDLE_PRIORITY_CLASS); ComboBox_SetCurSel(hWndCtrl, nNormal); //选中“标准”优先级 //线程相对优先级组合框 hWndCtrl = GetDlgItem(hwnd, IDC_THREADRELATIVEPRIORITY); n = ComboBox_AddString(hWndCtrl, TEXT("最高")); ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_TIME_CRITICAL); n = ComboBox_AddString(hWndCtrl, TEXT("高")); ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_HIGHEST); n = ComboBox_AddString(hWndCtrl, TEXT("高于标准")); ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_ABOVE_NORMAL); nNormal= n = ComboBox_AddString(hWndCtrl, TEXT("标准")); ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_NORMAL); n = ComboBox_AddString(hWndCtrl, TEXT("低于标准")); ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_BELOW_NORMAL); n = ComboBox_AddString(hWndCtrl, TEXT("空闲")); ComboBox_SetItemData(hWndCtrl, n, THREAD_PRIORITY_IDLE); ComboBox_SetCurSel(hWndCtrl, nNormal); Edit_LimitText(GetDlgItem(hwnd, IDC_SLEEPTIME), 4); //休眼最大值为9999 return TRUE; } ////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity) { switch (id) { case IDCANCEL: PostQuitMessage(0); break; case IDC_PROCESSPRIORITYCLASS: if (codeNotity == CBN_SELCHANGE){ int ntp = (int)ComboBox_GetItemData(hwndCtrl, ComboBox_GetCurSel(hwnd)); SetThreadPriority(GetCurrentThread(), ntp); } break; case IDC_THREADRELATIVEPRIORITY: if (codeNotity == CBN_SELCHANGE){ int ntp =(int)ComboBox_GetItemData(hwndCtrl, ComboBox_GetCurSel(hwnd)); SetThreadPriority(GetCurrentThread(),ntp); } break; case IDC_SUSPEND: //为了避免死锁,将“挂起”按钮禁用 EnableWindow(hwndCtrl, FALSE); HANDLE hThreadPrimary; //主线程 DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),&hThreadPrimary, THREAD_SUSPEND_RESUME,FALSE,DUPLICATE_SAME_ACCESS); DWORD dwThreadID; CloseHandle(chBEGINTHREADEX(NULL, 0, ThreadFunc, hThreadPrimary, 0, &dwThreadID)); break; } } ////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); } return FALSE; } ////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR szCmdLine, int) { HWND hWnd = CreateDialog(hInstExe, MAKEINTRESOURCE(IDD_SCHEDLAB), NULL, Dlg_Proc); //非模态对话框 BOOL fQuit = FALSE; while (!fQuit){ MSG msg; if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)){ //用来IsDialogMessage判断是否是键盘导航(如Tab键) if (!IsDialogMessage(hWnd,&msg)){ if (msg.message == WM_QUIT){ fQuit = TRUE; } else{ TranslateMessage(&msg); DispatchMessage(&msg); } } //End if(!IsDialogMessage()) } else{ //将数字加入列表框中 static int s_n = -1; TCHAR sz[20]; StringCchPrintf(sz, _countof(sz), TEXT("%u"), ++s_n); HWND hWndWork = GetDlgItem(hWnd, IDC_WORK); ListBox_SetCurSel(hWndWork, ListBox_AddString(hWndWork,sz)); //如果列表框中的项目太多时,则删除前100项 while (ListBox_GetCount(hWndWork) > 100) ListBox_DeleteString(hWndWork, 0); //获取线程休眠的时间 int nSleep = GetDlgItemInt(hWnd, IDC_SLEEPTIME, NULL, FALSE); if (chINRANGE(1,nSleep,9999)){ Sleep(nSleep); } } } DestroyWindow(hWnd); return 0; }
//resource.h
//{{NO_DEPENDENCIES}} // Microsoft Visual C++ 生成的包含文件。 // 供 7_SchedLab.rc 使用 // #define IDD_SCHEDLAB 101 #define IDI_SCHEDLAB 102 #define IDC_PROCESSPRIORITYCLASS 1001 #define IDC_THREADRELATIVEPRIORITY 1002 #define IDC_SLEEPTIME 1003 #define IDC_SUSPEND 1004 #define IDC_WORK 1005 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1006 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif
//SchedLab.rc
// Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "winres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS) LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE BEGIN "resource.h " END 2 TEXTINCLUDE BEGIN "#include ""winres.h"" " "