Windows Internals 笔记——线程调度
1.线程内核对象中的CONTEXT反应了线程上一次执行时CPU寄存器的状态。大约每隔20ms,Windows都会查看所有当前存在的线程内核对象。Windows在可调度的线程内核对象中选择一个,并将上次保存在线程上下文中的值载入CPU寄存器。这一操作被称为上下文切换。Windows实际上会记录每个线程的运行次数。
2.调用CreateProcess或者CreateThread时,系统将创建线程内核对象,并把挂起计数初始化为1。这样就不会给这个线程调度CPU了,因为线程初始化需要时间,我们不想再线程准备好之前就开始执行它。在线程初始化之后,CreateProcess或者CreateThread函数将查看是否有CREATE_SUSPENDED标志传入,如果有,函数返回并让新的线程处于挂起状态。如果没有,函数会将线程的挂起计数递减为0,线程就成为可调度的了。
3.通过创建一个处于挂起状态的线程,我们可以在线程执行任何代码之前改变它的环境(比如优先级)。之后可以调用ResumeThread函数使其变为可调度的,如果调用成功会返回线程的前一个挂起计数,否则返回0xFFFFFFFF。
4.还可以调用SuspendThread来挂起线程,任何线程都可以调用这个函数挂起另外一个线程(只要有线程句柄)。显然线程可以将自己挂起,但是它无法自己恢复。SuspendThread返回线程之前的挂起计数。一个线程最多可以挂起MAXIMUM_SUSPEND_COUNT(WinNT.H中定义为127)次。请注意,就内核模式下面执行情况而言,SuspendThread是异步的,但是在线程恢复之前,它是无法在用户模式下执行的。
5.调用SuspendThread时必须小心,因为试图挂起一个线程时,我们不知道线程在做什么,例如线程正在分配堆中的内存,线程将锁定堆,当其他线程要访问堆的时候,它们的执行将被终止,直到第一个线程恢复。所以可能会造成死锁。
6.Windows中不存在挂起和恢复进程的概念,因为系统从来不会给进程调度CPU时间。在一个特殊情况下,即调试器处理WaitForDebugEvent返回的调试事件时,Windows将冻结被调试进程中的所有线程。直至调试器调用ContinueDebugEvent。
7.调用Sleep函数可以告诉系统,在一定时间内自己不需要调度了。
- 调用Sleep函数,将使线程资源放弃属于它的时间片中剩下的部分。
- 系统设置线程不可调度的时间只是“近似于”所设定的毫秒数,实际情况取决于系统中其他线程的运行情况。
- 给Sleep传递INFINITE参数,告诉系统永远不要调度这个进程,这样做没什么用。
- 可以给Sleep传入0,这是在告诉系统,主调线程放弃了时间片的剩余部分,它强制系统调度其他线程。但是系统有可能重新调度刚刚调用Sleep的那个线程,如果没有相同或者优先级较高的可调度线程时,就会发生这样的事情。
8.调用SwitchToThread函数时,系统查看是否存在正急需CPU时间的饥饿线程,如果没有SwitchToThread立即返回,如果存在将调度该线程(其优先级可能比SwitchToThread的主调线程低)。饥饿线程可以运行一个时间量,然后系统调度程序恢复正常运行。
9.超线程处理器芯片有多个“逻辑”CPU,每个都可以运行一个线程。每个线程都有自己的体系结构状态(一组寄存器),但是所有线程共享主要的执行资源,比如CPU高速缓存。当一个线程终止时,CPU自动执行另一个线程,无需操作系统干预,只有在缓存未命中、分支预测错误和需要等待前一个指令的结果等情况下,CPU才会暂停。
10.在超线程CPU上执行旋转循环(spin loop)时,需要我们强制当前线程暂停,使另一个线程可以访问芯片的资源。x86体系结构支持一个名为PAUSE的汇编语言指令。PAUSE指令可以确保避免内存顺序违规,从而改进性能。在x86上,PAUSE指令等价于REP NOP指令。PAUSE会导致一定的延时(有些CPU上为0).在Win32 API中,x86 PAUSE指令是通过调用WinNT.h中定义的YieldProcessor宏发出的。
11.用GetTickCount()或GetTickCount64()来计算某项任务消耗时间的时候,其前提是该任务执行时不会被中断,但是Windows是抢占式的操作系统。在Windows Vista之前的系统中我们就可以使用GetThreadTimes()函数。GetProcessTimes的返回值是进程中所有线程(即使线程已经终止)的时间综总和。
12.在Windows Vista中,系统为线程分配CPU时间的方式发生了改变。操作系统不再依赖约10~15ms的间隔时钟计时器,而是改用处理器的64位时间戳计时器,它计算的是机器启动以来的时钟周期数。QueryThreadCycleTime()和QueryProcessCycleTime()函数分别返回给定线程或进程的所有线程所用的时钟周期数。
13.在Windows定义的所有数据结构中,CONTEXT结构是唯一一个特定于CPU的。所以它的成员的具体情况取决于Windows运行在什么CPU上。以下是x86 CPU的完整CONTEXT结构定义:
typedef struct _CONTEXT { //
// The flags 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 threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then 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 the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
// FLOATING_SAVE_AREA FloatSave; //
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
// DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs; //
// This section is specified/returned if the
// ContextFlags word contians 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 contians 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结构分为几部分:
- CONTEXT_CONTROL包含CPU的控制寄存器,比如指令指针、栈指针、标志和函数返回地址。
- CONTEXT_INTEGER标识CPU的整数寄存器。
- CONTEXT_FLOATING_POINT标识CPU的浮点寄存器。
- CONTEXT_SEGMENTS标识CPU的段寄存器。
- CONTEXT_DEBUG_REGISTERS标识CPU的调试寄存器。
- CONTEXT_EXTENDED_REGISTERS标识CPU的扩展寄存器。
14.Windows实际上允许我们调用GetThreadContext查看线程的内核对象的内部,并获取当前CPU寄存器状态的集合。调用之前应先调用SuspendThread,否则,系统可能正好获得调度此线程,这样线程的上下文与所获取的信息就不一致了。
15.一个线程实际上有两个上下文:用户模式和内核模式。GetThreadContext只能返回线程的用户模式的上下文。如果调用SuspendThread暂停一个线程,但是该线程正在内核模式执行,那么它的用户模式上下文保持不变,即使SuspendThread实际上还没有暂停线程。但是,在线程恢复之前,不能再执行任何用户模式的代码,因此,我们完全可以认为线程已经暂停,这时调用GetThreadContext是非常安全的。
16.CONTEXT结构的ContextFlags成员与任何CPU寄存器都不对应。这个成员的作用是告诉GetThreadContext函数应该获取哪些寄存器。请注意在调用GetThreadContext之前,必须首先初始化CONTEXT结构的ContextFlags成员。
17.Windows还允许我们通过调用SetThreadContext来改变结构中的成员,并把新的寄存器放回线程的内核对象中。同样调用之前要先SuspendThread,否则结果无法预料。也必须再初始化CONTEXT的ContextFlags成员。
Windows Internals 笔记——线程调度的更多相关文章
- Windows Internals 笔记——进程
1.一般将进程定义成一个正在运行的程序的一个实例,由以下两部分构成: 一个内核对象,操作系统用它来管理进程,内核对象也是系统保存进程统计信息的地方. 一个地址空间,其中包含所有可执行文件或DLL模块的 ...
- Windows Internals 笔记——关联性
1.默认情况下,Windows Vista在给线程分配处理器时,使用软关联.意思是如果其他因素都一样,系统将使线程在上一次运行的处理器上运行.让线程始终在同一个处理器上运行有助于重用仍在处理器高速缓存 ...
- Windows Internals 笔记——线程优先级
1.每个线程都被赋予0(最低)~31(最高)的优先级数.当系统确定给哪个线程分配CPU时,它会首先查看优先级为31的线程,并以循环的方式进行调度.如果有优先级为31的线程可供调度,那么系统就会将CPU ...
- Windows Internals 笔记——线程
1.进程有两个组成部分,一个进程内核对象和一个地址空间.线程也有两个组成部分: 一个是线程的内核对象,操作系统用它管理线程.系统还用内核对象来存放线程统计信息的地方. 一个线程栈,用于维护线程执行时所 ...
- Windows Internals 笔记——进程的权限
1.大多数用户都用一个管理员账户来登录Windows,在Vista之前,这样的登录会创建一个安全令牌.每当有代码试图使用一个受保护的安全资源时,操作系统就会出示这个令牌.从包括Windows资源管理器 ...
- Windows Internals 笔记——作业
1.Windows提供了一个作业内核对象,它允许我们将进程组合在一起并创建一个“沙箱”来限制进程能够做什么.创建只包含一个进程的作业同样非常有用,因为这样可以对进程施加平时不能施加的限制. 2.如果进 ...
- Windows Internals 笔记——CreateProcess
1.一个线程调用CreateProcess时,系统将创建一个进程内核对象,其初始使用计数为1.然后系统为新进程的主线程创建一个线程内核对象(使其计数为1). 2.CreateProcess在进程完全初 ...
- Windows Internals 笔记——内核对象
1.每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问.这个内存块是一个数据结构,其成员维护着与对象相关的信息. 2.调用一个会创建内核对象的函数后,函数会返回一个句柄,它标 ...
- Windows Internals 笔记——字符和字符串处理
1.自Windows NT起,Windows的所有版本都完全用Unicode来构建,调用Windows函数时,如果向它传入一个ANSI字符串,那么函数首先会把字符串转换为Unicode,再把结果传给操 ...
随机推荐
- 我的python之路
一.基础语法 Python基础—基本语法结构 Python基础—程序控制结构 Python基础—基本数据类型 Python基础—文件的读写操作 二.函数 Python基础—初识函数 Python基础— ...
- P1162 填涂颜色
原题链接 https://www.luogu.org/problemnew/show/P1162 一道很水很简单的搜索题,好吧我还是交了4次才过的...... 说一下简单的思路: 首先输入n*n的矩阵 ...
- 快速去水印(win10换图3D工具)
之前抠图都用ps啥的,后来发现win10自带的工具画图3D可以直接扣简单的图案,达到去水印的效果 1.将图片放入软件中 2.使用神奇选择工具,调整大小,框出图标 3.点击下一步,将没选上的或选多的进行 ...
- 树莓派中QT实现I2C
树莓派中QT实现I2C 在QT中实现 I2C 使用的驱动为 wiringPi 库的引入 LIBS += -lwiringPi LIBS += -lwiringPiDev 代码实现 widget.h 中 ...
- Beyas定理
\(Beyas\)定理 首先由条件概率的计算式有 \[Pr\{A|B\}=\frac{Pr\{A\cap B\}}{Pr\{B\}}\] 结合交换律得到 \[Pr\{A\cap B\}=Pr\{B\} ...
- 超越村后端开发(2:新建models.py+xadmin的引入)
1.新建Model 1.users数据 1.在apps/users/models.py中: from datetime import datetime from django.db import mo ...
- PMP是什么,PMP最难的是哪些内容?
目前在国内很多人还不了解PMP是什么,甚至不知道PMP认证的内容是哪些,考来有什么用,今天我这边就普及一下. PMP认证是美国项目管理协会发起的一项针对项目管理专业人士资格认证.取得认证需要学习项目管 ...
- bootstrap: 内联表单;
<form class="form-inline"> <div class="form-group"> <label for=&q ...
- mac-redis安装与使用
安装: brew install redis --------------- 使用: 启动redis-server: sudo redis-server 连接:./redis-cli -h 127.0 ...
- 重新安装了环境报错{"error":"could not find driver"}
前言:最近新工作开发oa系统,没有借助工具安装wamp环境,结果在测试项目时候出现了bug,找了很久,发现方向没有对 报错信息: {"error":"could not ...