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内核之线程的调度,优先级,亲缘性,布布扣,bubuko.com

时间: 2024-12-07 17:31:01

Windows内核之线程的调度,优先级,亲缘性的相关文章

Windows内核之线程简单介绍

1 线程定义 <1> 内核对象,操作系统用它来对线程实施管理.内核对象也是系统用来存放线程统计信息的地方 <2>还有一个是线程堆栈.它用于维护线程在运行代码时须要的全部函数參数和局部变量. 2 线程和进程的差别 <1>进程是不活泼的. 进程从来不运行不论什么东西.它仅仅是线程的容器. <2>线程在它的进程地址空间中运行代码,而且在进程的地址空间中对数据进行操作. <3>线程共享进程的地址控件 <4>线程仅仅有一个内核对象和一个堆栈,保

Linux有内核级线程吗?

线程通常被定义为一个进程中代码的不同执行路线.从实现方式上划分,线程有两种类型:"用户级线程"和"内核级线程". 用户线程指不需要内核支持而在用户程序中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建.同步.调度和管理线程的函数来控制用户线程.这种线程甚至在象 DOS 这样的操作系统中也可实现,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务.另外一种则需要内核的参与,由内核完成线程的调度.其依赖于操作系统核心,由内核的

回炉重造之重读Windows核心编程-007-线程的调度、优先级与亲缘性

Windows被设计成一个抢占式的操作系统,用某种算法来确定哪些线程应该在何时被调度和运行多长时间.每隔20ms左右,Windows就要查看当前所有线程的内核对象,找到可以被调度的一个,将它加载到CPU寄存器中.这个操作成为上下文切换.Windows实际上保存了一个记录,说明每个线程获得了多少次运行的机会.使用MicrosoftSpy++这个工具可以了解这个情况. 一个线程随时可以停止运行,一个线程可以进行调度.可以对线程进行一定程度的控制,但是不能太多.不能保证一个线程做任何事. 7.1暂停和

Windows API学习---线程与内核对象的同步

前言 若干种内核对象,包括进程,线程和作业.可以将所有这些内核对象用于同步目的.对于线程同步来说,这些内核对象中的每种对象都可以说是处于已通知或未通知的状态之中.这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的.例如,进程内核对象总是在未通知状态中创建的.当进程终止运行时,操作系统自动使该进程的内核对象处于已通知状态.一旦进程内核对象得到通知,它将永远保持这种状态,它的状态永远不会改为未通知状态. 当进程正在运行的时候,进程内核对象处于未通知状态,当进程终止运行的时候,它就变

用户空间实现线程 内核实现线程 线程的调度

http://blog.csdn.net/lujiandong1/article/details/44726373 1.在用户空间中实现线程 (1)特点:把整个线程包放在用户空间,内核对线程包一无所知.从内核角度考虑,就是按正常的方式管理,即单线程进程(存在运行时系统) (2)优点: 1.用户级线程包可以在不支持线程的操作系统上实现. 2.线程切换至少要比陷入内核要快一个数量级.在线程完成运行时,它调用thread_yield可以把该线程的信息保存在线程表中:进而,它可以调用线程调度程序来选择另

C++windows内核编程笔记day13 进程、线程与信号量

Windows进程 进程是一个容器,包含程序执行需要的代码.数据.资源等信息, windows进程的特点: 每个进程都有自己的ID号 每个进程都有自己的地址空间,进程之间无法访问对方的地址空间. 每个进程都有自己的安全属性 每个进程至少包含一个线程. 获取和释放环境信息 GetEnvironmentStrings FreeEnvironmentStrings 获取或设置 本程序的环境变量 GetEnvironmentVariable SetEnvironmentVariable 示例: char

多线程入门-第三章-线程的调度与控制之优先级

包括内容:优先级.sleep.yield.join 通常计算机只有一个cpu,cpu在某一时刻只能执行一条指令,线程只有得到CPU时间片,才能执行指令.在单cpu的机器上线程表示并行运行的,只有多个CPU上线程才可以并行运行.JVM要负责线程的调度,取得CPU的使用权.目前有两种调度模型:分时调度模型和抢占式调度模型,Java使用抢占式调度模型. 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用cpu的时间片: 抢占式调度模型:优先让优先级高的线程使用cpu,如果线程的优先级相

C++windows内核编程笔记day14 其他线程同步技术

线程同步技术: 原子锁 临界区(段) 互斥 事件 信号量(线程示例时已经使用过) 可等候定时器 使用范围:原子锁<临界区<互斥 效率:    原子锁>临界区(用户态)>互斥(内核态) 一般用临界区. //等候多个信号 DWORD WaitForMultipleObjects( DWORD nCount,             // number of handles in array CONST HANDLE *lpHandles,  // object-handle array

windows内核窥探

windows是一个非常优秀的OS,从今天开始,我要和大家共同分享windows给我们带来的快乐!本人只所以将自己的学习笔记与大家分享,一是让自己更深入的理解windows,再就是有什么疏漏之处,望大家指正!!来吧,开始我们的windows之旅! 一,windows2000体系结构(1)系统模型   在大多数多用户的OS中,用户程序和系统程序是分开的---系统程序是一个比较高的优先级上运行(核心态),而用户程序是在一个较低的等级上运行.系统程序有对系统数据和硬件的操作权,而用户程序要想操作系统数