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 笔记——线程调度的更多相关文章

  1. Windows Internals 笔记——进程

    1.一般将进程定义成一个正在运行的程序的一个实例,由以下两部分构成: 一个内核对象,操作系统用它来管理进程,内核对象也是系统保存进程统计信息的地方. 一个地址空间,其中包含所有可执行文件或DLL模块的 ...

  2. Windows Internals 笔记——关联性

    1.默认情况下,Windows Vista在给线程分配处理器时,使用软关联.意思是如果其他因素都一样,系统将使线程在上一次运行的处理器上运行.让线程始终在同一个处理器上运行有助于重用仍在处理器高速缓存 ...

  3. Windows Internals 笔记——线程优先级

    1.每个线程都被赋予0(最低)~31(最高)的优先级数.当系统确定给哪个线程分配CPU时,它会首先查看优先级为31的线程,并以循环的方式进行调度.如果有优先级为31的线程可供调度,那么系统就会将CPU ...

  4. Windows Internals 笔记——线程

    1.进程有两个组成部分,一个进程内核对象和一个地址空间.线程也有两个组成部分: 一个是线程的内核对象,操作系统用它管理线程.系统还用内核对象来存放线程统计信息的地方. 一个线程栈,用于维护线程执行时所 ...

  5. Windows Internals 笔记——进程的权限

    1.大多数用户都用一个管理员账户来登录Windows,在Vista之前,这样的登录会创建一个安全令牌.每当有代码试图使用一个受保护的安全资源时,操作系统就会出示这个令牌.从包括Windows资源管理器 ...

  6. Windows Internals 笔记——作业

    1.Windows提供了一个作业内核对象,它允许我们将进程组合在一起并创建一个“沙箱”来限制进程能够做什么.创建只包含一个进程的作业同样非常有用,因为这样可以对进程施加平时不能施加的限制. 2.如果进 ...

  7. Windows Internals 笔记——CreateProcess

    1.一个线程调用CreateProcess时,系统将创建一个进程内核对象,其初始使用计数为1.然后系统为新进程的主线程创建一个线程内核对象(使其计数为1). 2.CreateProcess在进程完全初 ...

  8. Windows Internals 笔记——内核对象

    1.每个内核对象都只是一个内存块,它由操作系统内核分配,并只能由操作系统内核访问.这个内存块是一个数据结构,其成员维护着与对象相关的信息. 2.调用一个会创建内核对象的函数后,函数会返回一个句柄,它标 ...

  9. Windows Internals 笔记——字符和字符串处理

    1.自Windows NT起,Windows的所有版本都完全用Unicode来构建,调用Windows函数时,如果向它传入一个ANSI字符串,那么函数首先会把字符串转换为Unicode,再把结果传给操 ...

随机推荐

  1. 前端动态菜单-bootstrap-treeview

    一.bootstrap-treeview 官网 Demo bootstrap-treeview是一款效果非常酷的基于bootstrap的jQuery多级列表树插件.该jQuery插件基于Twitter ...

  2. 关于【jq插件开发】

    很详细,原文链接:https://www.cnblogs.com/Wayou/p/jquery_plugin_tutorial.html#commentform和https://www.cnblogs ...

  3. BUG in Ubuntu--Could not get lock /var/lib/dpkg/lock

    在ubuntu中通过apt安装软件时,报错: E: Could not : Resource temporarily unavailable) E: Unable to lock the admini ...

  4. eclipse JVM 性能调优

    最近因项目存在内存泄漏,故进行大规模的JVM性能调优 , 现把经验做一记录. 一.JVM内存模型及垃圾收集算法 1.根据Java虚拟机规范,JVM将内存划分为: New(年轻代) Tenured(年老 ...

  5. ElasticSearch常用操作

    查看某个INDEX库某个TYPE表,某个字段的分词结果  GET /${index}/${type}/${id}/_termvectors?fields=${fields_name}http://19 ...

  6. 利用java内部静态类实现懒汉式单例

    /** * @Description: 利用键值模式控制service * @Author: zhanglifeng * @Date: 2019年 04月 28日 14:41 **/ public c ...

  7. mkdir(): Permission denied

    记录下,凡是遇到此类问题都是无权限导致. 根据不同场景,需要在不同的文件夹设置不同的权限. 例如,图片相关, 在php中,首先看下 配置php.ini的文件上传是否开启(file_uploads = ...

  8. windows10安装docker,运行jhipster-registry

    1.官网下载windows版docker 2.设置bios 3.CMD进入到某个jhipster的工程目录,执行"docker-compose -f src/main/docker/jhip ...

  9. day20面向对象三大特性 , 类嵌套

    #!/usr/bin/env python# -*- coding:utf-8 -*- # 1.简述面向对象三大特性并用代码表示."""封装:class Account: ...

  10. [译]Ocelot - Caching

    原文 Ocelot支持基本的缓存,目前Ocelot的缓存是通过CacheManager project实现的. 下面的示例展示了如何启用缓存: s.AddOcelot() .AddCacheManag ...