Windows内核之线程的调度,优先级,亲缘性
1 调度
Windows不是实时操作系统,它是抢占式多线程操作系统。在如果全部优先级同样的情况下,CPU对线程的调度原则是每隔20m就会切换到下一个线程,依据Context中的IP和SP来接着运行上次的东西。Windows永远不会让1个线程去独占一段时间。
2 可调度性
系统仅仅调用能够调度的线程,事实上系统的大部分线程都是处于不可调度的状态,要么处于暂停的状态,要么处于休眠的状态。
3 线程的暂停和恢复
<1>在CreateThread的时候通过制定CREATE_SUSPENDED来让线程暂停运行
<2>在DWORD SuspendThread(HANDLE hThread)函数来暂停一个线程,最多次数为MAXIMUM_SUSPEND_COUNT(127)
<3>通过函数DWORD ResumeThread(HANDLE hThread)来唤醒线程
4 进程的暂停
Windows中从来不存在进程的暂停和恢复,由于进程是不会被调度的。可是在特殊情况下Windows会冻结进程中全部的线程:调试程序中处理函数WaitForDebugEvent返回的debug事件;直到调用ContinueDebugEvent.函数才会恢复进程中的全部线程。
可是我们能够通过遍历系统中全部的线程,通过检查线程所属的进程ID是否满足指定值,就能够做到暂停全部的线程。
弊端:
<1>遍历线程ID时候,假设有新线程在创建,那么新线程将不会被暂停
<2>当又一次恢复线程的时候,可能会对新创建的没有被暂停的线程去恢复
<3>遍历线程ID的时候,撤销的线程跟新建的线程可能具有具有同样的ID,这就可能导致暂停多个具有同样ID的线程。
进程中全部线程暂停函数例如以下所看到的:
VOID SuspendProcess(DWORD dwProcessID, BOOL fSuspend) {
// Get the list of threads in the system.
HANDLE hSnapshot = CreateToolhelp32Snapshot(
TH32CS_SNAPTHREAD, dwProcessID);
if (hSnapshot != INVALID_HANDLE_VALUE) {
// Walk the list of threads.
THREADENTRY32 te = { sizeof(te) };
BOOL fOk = Thread32First(hSnapshot, &te);
for (; fOk; fOk = Thread32Next(hSnapshot, &te)) {
// Is this thread in the desired process?
if (te.th32OwnerProcessID == dwProcessID) {
// Attempt to convert the thread ID into a handle.
HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME,
FALSE, te.th32ThreadID);
if (hThread != NULL) {
// Suspend or resume the thread.
if (fSuspend)
SuspendThread(hThread);
else
ResumeThread(hThread);
}
CloseHandle(hThread);
}
}
CloseHandle(hSnapshot);
}
}
5 进程的休眠
VOID Sleep(DWORD dwMilliseconds)
<1>线程的休眠导致线程在一定时间段内放弃被调度的机会
<2>线程休眠的时间大约是指定的时间,可是可能远大于这个时间,这取决于操作系统
<3>參数值为INFINITE,表示系统永远不去调度线程,可是这种方法不好
<4>參数为0,表示放弃此次的时间片,切换到下一个线程,可是线程可能切换到自身假设没有同等优先级或者更高的优先级的存在。
6 线程的切换
BOOL SwitchToThread();
当调用这个函数的时候,系统检測是否有一个线程迫切需求CPU时间,假设没函数就马上返回,假设有就切换到这个线程,即便线程的优先级可能低于当前的线程优先级。
函数的功能和Sleep函数在參数值为0的时候非常相似,可是不同点是SwitchToThread函数同意优先级低的线程被调用,Sleep函数却不行。
7 线程的运行时间
<1>通常的程序执行时间计算方法:
ULONGLONG qwStartTime = GetTickCount64();
// Perform complex algorithm here.
ULONGLONG qwElapsedTime = GetTickCount64()- qwStartTime;
可是这样事实上是错误的,由于它如果线程运行不被为中断。
<2>Windows提供了一个获取线程和进程时间信息的函数GetThreadTime, GetProcessTime
BOOL GetThreadTimes(
HANDLE hThread,
PFILETIME pftCreationTim
PFILETIME pftExitTime,
PFILETIME pftKernelTime,
PFILETIME pftUserTime); BOOL GetProcessTimes(
HANDLE hProcess,
PFILETIME pftCreationTime,
PFILETIME pftExitTime,
PFILETIME pftKernelTime,
PFILETIME pftUserTime);
<3>TSC 计时方法
眼下线程的计时时间方式发生了变换,和之前精度为10-15ms的内部时钟计时器不同,系统如今採用一种Time Stamp Counter (TSC)计算时间,它表示的是自从计算机开机后执行的CPU周期个数。
通过QueryThreadCycleTime和QueryProcessCycleTime来获取线程和进程运行的周期个数。
BOOL WINAPI QueryThreadCycleTime(
_In_ HANDLE ThreadHandle,
_Out_ PULONG64 CycleTime //包括用户时间和内核时间总和的周期计数值
);
BOOL WINAPI QueryProcessCycleTime(
_In_ HANDLE ProcessHandle,
_Out_ PULONG64 CycleTime //包括用户时间和内核时间总和的周期计数值
);
<4>高精度计时方法
BOOLQueryPerformanceFrequency(LARGE_INTEGER* pliFrequency);
BOOL QueryPerformanceCounter(LARGE_INTEGER*pliCount);
可是若用这两个函数来计算线程的运行时间的话,前提是如果线程不被抢占。
8 Context的使用
我们说Context中存放着线程的状态信息,同意线程在调用时候继续上次的运行。CONTEXT结构体是唯一的依赖于CPU的结构体。比如在X86体系结构中,它包括以下寄存器。
CONTEXT_CONTROL,CONTEXT_DEBUG_REGISTERS,CONTEXT_FLOATING_POINT,CONTEXT_SEGMENTS,CONTEXT_INTEGER,CONTEXT_EXTENDED_REGISTERS
比如在x86中,它例如以下所看到的:
typedef struct _CONTEXT {
//
// The flag values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a thread's context, only that
// portion of the thread's context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//
DWORD ContextFlags;
//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
//
DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;
// This section is specified/returned if theContextFlags word contains the flag //CONTEXT_FLOATING_POINT.
FLOATING_SAVE_AREA FloatSave;
//
// This section is specified/returned if the
// ContextFlags word contains the flag CONTEXT_SEGMENTS.
//
DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;
//
// This section is specified/returned if the
// ContextFlags word contains the flag CONTEXT_INTEGER.
//
DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;
//
// This section is specified/returned if the
// ContextFlags word contains the flag CONTEXT_CONTROL.
//
DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;
//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//
BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];
} CONTEXT;
我们能够对CONTEXT中寄存器的内容进行读取和改写,这是相当酷比的行为。
<1> 获取CONTEXT内容
BOOL GetThreadContext(
HANDLE hThread,
PCONTEXT pContext);
演示样例:
CONTEXT Context;
Context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &Context);
<2>设置CONTEXT内容
BOOL SetThreadContext(
HANDLE hThread,
CONST CONTEXT *pContext);
演示样例:
CONTEXT Context;
SuspendThread(hThread);
Context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &Context);
Context.Eip = 0x00010000;
Context.ContextFlags = CONTEXT_CONTROL;
SetThreadContext(hThread, &Context);
ResumeThread(hThread);
9 线程的优先级
<1>每一个线程都会被赋予编号为0-31的一个优先级别,31表示最高,0表示最低
<2>仅仅要有优先级为31的可调用,就绝不会调用0-30的
<3>即便低优先级正在执行,仅仅要系统发现一个高的优先级要执行,低的会被暂停。
<4>当系统引导时会创建一个特殊线程叫做0页线程,是系统中唯一优先级为0的线程,当系统没有别的线程执行时候,0页线程会负责将系统全部空暇RAM页面置0.
10 优先级的抽象说明
应用程序的作用是不断变化的,需求也是不断变化的,今天设置的优先级可能在在明天已经不合时宜,为了能使今天写的程序能在以后的系统上正常的执行,所以调度算法不能一成不变,因此为微软给应用程序设置了一个抽象的优先级别,Windows支持6种优先级别
通过这样的方式,能够简单改变程序的优先级别就达到改变程序中全部线程优先级别的作用。在应用程序的基础上再依据线程的优先级别,给线程设计优先类,一共7个例如以下所看到的:
那么线程结合进程后,线程的优先级例如以下所看到的:
注意:进程没有被调度,实质上没有优先级可言,这里说的进程的优先级仅仅是个抽象概念,通过这个抽象的优先级,能够改变线程的优先级。
11 设置程序的优先级
<1>CreateProcess的时候,dwCreationFlags參数能够设置
<2> 子进程在执行的时候改变优先级
BOOL SetPriorityClass(HANDLE hProcess, DWORD fdwPriority);
<3>命令行启动程序
当正常启动时候,默认进程是正常的优先级,当用STRAT启动进程的时候,能够附带优先级开关。例如以下所看到的:
C:\>START /LOW CALC.EXE
/BELOWNORMAL, /NORMAL, /ABOVENORMAL,/HIGH, /REALTIME,这些都是可选的模式
<4> 通过任务管理器来设置进程的优先级别
<5>设置线程优先级
BOOL SetThreadPriority( HANDLE hThread, int nPriority);
线程刚刚创建的时候,优先级是默认的正常优先级,设置优先级的代码例如以下所看到的:
DWORD dwThreadID;
HANDLE hThread = CreateThread(NULL, 0,ThreadFunc, NULL,
CREATE_SUSPENDED, &dwThreadID);
SetThreadPriority(hThread,THREAD_PRIORITY_IDLE);
ResumeThread(hThread);
CloseHandle(hThread)
<6> 动态提高线程优先级
线程的基本优先级:线程的相对优先级和线程所属的进程的优先级综合考虑得到的优先级
系统经常要提高线程的优先级等级,以便对窗体消息或读取磁盘等I / O事件作出响应。
系统仅仅能为基本优先级等级在1至1 5之间的线程提高其优先级等级
线程的当前优先级不会低于线程的基本优先级
系统决不会将线程的优先级等级提高到实时范围(高于 1 5)
假设要拒绝操作系统动态的提高线程的优先级,那么就能够使用以下的两个函数:
BOOL SetProcessPriorityBoost(HANDLEhProcess, BOOLbDisablePriorityBoost);
BOOL SetThreadPriorityBoost(HANDLE hThread,BOOLbDisablePriorityBoost);
检查是否启动自己主动调整优先级,使用以下的两个函数:
BOOL GetProcessPriorityBoost(HANDLEhProcess,PBOOL pbDisablePriorityBoost);
BOOL GetThreadPriorityBoost(HANDLE hThread,PBOOLpbDisablePriorityBoost);
<7>为前台进程调整调度程序
当用户对进程的窗体进行操作时,该进程就称为前台进程,全部其它进程则称为后台进程。当然,用户希望他正在使用的进程比后台进程具有更强的响应性。为了提高前台进程的响应性,Wi n d o w s可以为前台进程中的线程调整其调度算法。对于 Windows 2000来说,系统可以为前台进程的线程提供比通常多的 C P U时间量。这样的调整仅仅能在前台进程属于正常优先级类的进程时才干进行。假设它属于其它不论什么优先级类,就无法进行不论什么调整。
当一个优先级为正常的进程移到前台时,系统便将最低、低于正常、正常、高于正常和最高等优先级的线程的优先级提高 1,优先级为空暇和关键时间的线程的优先级则不予提高。
设置是否開始提高前台调度性能的方法:
10 亲缘性
这个不多说了,就是在有多个CPU的时候,指定进程或者线程在哪几个指定的CPU上执行的策略,在一定情况下能够提高CPU的使用率。
Windows内核之线程的调度,优先级,亲缘性的更多相关文章
- Java多线程-线程的调度(优先级)
与线程休眠类似,线程的优先级仍然无法保障线程的执行次序.只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行. 线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先 ...
- Windows内核之线程简单介绍
1 线程定义 <1> 内核对象,操作系统用它来对线程实施管理.内核对象也是系统用来存放线程统计信息的地方 <2>还有一个是线程堆栈.它用于维护线程在运行代码时须要的全部函数參数 ...
- Windows核心编程 第七章 线程的调度、优先级和亲缘性(下)
7.6 运用结构环境 现在应该懂得环境结构在线程调度中所起的重要作用了.环境结构使得系统能够记住线程的状态,这样,当下次线程拥有可以运行的C P U时,它就能够找到它上次中断运行的地方. 知道这样低层 ...
- 【windwos 操作系统】关键的Windows内核数据结构一览(上)
文章作者:r00tk1t 发布时间:2018年01月08日 - 21时56分 最后更新:2020年10月20日 - 21时01分 原始链接:https://r00tk1ts.github.io/201 ...
- Windows核心编程 第七章 线程的调度、优先级和亲缘性(上)
第7章 线程的调度.优先级和亲缘性 抢占式操作系统必须使用某种算法来确定哪些线程应该在何时调度和运行多长时间.本章将要介绍Microsoft Windows 98和Windows 2000使用的一些算 ...
- [5]windows内核情景分析---进程线程
本篇主要讲述进程的启动过程.线程的调度与切换.进程挂靠 进程的启动过程: BOOL CreateProcess ( LPCTSTR lpApplicationName, ...
- windows核心编程---第六章 线程的调度
每个线程都有一个CONTEXT结构,保存在线程内核对象中.大约每隔20ms windows就会查看所有当前存在的线程内核对象.并在可调度的线程内核对象中选择一个,将其保存在CONTEXT结构的值载入c ...
- Windows API学习---线程与内核对象的同步
前言 若干种内核对象,包括进程,线程和作业.可以将所有这些内核对象用于同步目的.对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中.这种状态的切换是由Microsoft为 ...
- Windows Internals 笔记——线程优先级
1.每个线程都被赋予0(最低)~31(最高)的优先级数.当系统确定给哪个线程分配CPU时,它会首先查看优先级为31的线程,并以循环的方式进行调度.如果有优先级为31的线程可供调度,那么系统就会将CPU ...
随机推荐
- ExecuteNonQuery&& ExecuteQuery 区别
前些日子作一些数据项目的时候 在ADO.NET 中处理 ExecuteNonQuery()方法时,总是通过判断其返回值是否大于0来判断操作时候成功 .但是实际上并不是这样的,好在处理的数据操作多时 修 ...
- odoo 错误 Resource interpreted as Stylesheet but transferred with MIME type application/x-css:
odoo8 页面内容显示一半, web 控制台显示错误 Resource interpreted as Stylesheet but transferred with MIME type ap ...
- wcf安全
http://www.cnblogs.com/artech/archive/2011/07/07/customauthorization01.html 安全 http://www.cnblogs.co ...
- nutch 二次开发
/*深度控制*/ 深度控制:nutch是广域网的深度遍历,我们需要的是垂直采集(即只采集某一个栏目),举例,索引页总计20页,如果只有下一页,则深度为20,如果是1 2 3 4 5……20则深度为2即 ...
- POJ Code the Tree 树的pufer编号
Code the Tree Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 2259 Accepted: 859 Desc ...
- Musical Theme
poj1743:http://poj.org/problem?id=1743 题意:题意抽象出来就是给你一个序列,然后找一个长度不少于5的没有重复的等差数列. 题解:每相邻的两个数做差,然后转化成求字 ...
- LeetCode_3 sum closet
Given an array S of n integers, find three integers in S such that the sum is closest to a given num ...
- spring 动态数据源
1.动态数据源: 在一个项目中,有时候需要用到多个数据库,比如读写分离,数据库的分布式存储等等,这时我们要在项目中配置多个数据库. 2.原理: (1).spring 单数据源获取数据连接过程: ...
- Android数据存储之文件存储
首先给大家介绍使用文件如何对数据进行存储,Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的. public ...
- 【HDOJ】2782 The Worm Turns
DFS. /* 2782 */ #include <iostream> #include <queue> #include <cstdio> #include &l ...