• 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成员。

  • 相关阅读:
    Vue 配置多页面,去掉.html后缀的技巧
    uniapp img header 请求图片
    vscode 配置搜索过滤目录
    elementui eltable 开发环境 无法渲染,无法显示的问题
    isVnode 判断一个对象是否为 vnode 类型
    Cache缓存帮助类
    网络(NET)帮助类
    解读event.returnValue和return false
    sql server 生成连续日期和数字
    C#中对象与JSON字符串互相转换的三种方式
  • 原文地址:https://www.cnblogs.com/zoneofmine/p/8807929.html
Copyright © 2020-2023  润新知