输出倒逼输入系列之线程上下文切换的知识

背景

继上次讲完了什么是线程"等待"状态(即操作系统线程挂起状态),这里想再详细讲一讲,线程挂起的时候,背后具体会进行哪些操作

这里我们主要分三个部分来进行阐述

一 进程挂起具体执行逻辑

操作系统提供分层的权限机制,把区域分为 用户态 和 内核态

线程挂起的时候,其中核心逻辑为-用户态主动执行操作系统提供的schedule() 方法,从用户态进入内核态

1 用户态的寄存器信息据压入 内核栈

操作系统寄存器:

CPU 是真正干活的,它干活时用很多寄存器来存储它的控制信息,计算信息,以及数据信息 数据信息:通用寄存器 来存储计算过程中暂存数据

控制信息:

CS :代码段寄存器,代码在内存的初始位置

DS: 数据段的寄存器,初始数据等存储位置

SS: 栈寄存器,函数调用栈存储位置

IP:指令指针寄存器

ESP: 栈顶指针寄存器

代码段的偏移量在 IP 寄存器中,数据段的偏移量会放在 通用寄存器中

业务开发的时候,会经常碰到函数调用,这里详细解释一下用户态函数栈

用户态函数栈 SS:

栈帧如图:

组成机构:

1 上一个 栈帧的 ebp 地址

2 局部变量

3 调用方法所用的参数

4 返回地址

这里可以重点了解下:

假如 A 调用了 B,B 通过它的栈帧中的上一个栈帧的EBP地址,找到 A 传进来的参数,且当 B 运行完毕,返回值会保存在 EAX 寄存器中

把上述用户态所有寄存器信息全部压入 内核stack

内核栈 stack 数据结构:

如图:

pt_regs 里有当时用户态运行的所有上下文信息

内核 stack最关键的作用,其实就是用来保存此时这个用户态的上下文。且当执行完操作系统的调用函数时,可以从这里拿取信息恢复用户态上下文,继续运行上次停下的逻辑

2 从队列中选出继任者进程

调度任务策略:

先进先出

轮流调度

完全公平等策略

这里主要介绍,普通进程的安全公平策略

首先每一个任务都有一个 vruntime ,vruntime 代表可分配的运行时间。把任务想关信息封装成一个 scheduler_entity,再放进红黑树进行排序,调度时每次选出最小的 vruntime 任务,放入CPU去执行

CPU 运行时:

线程挂起的时候,从 CPU的相应的队列中,选出下一个任务

每一个 CPU都有一个rq ,rq 里分实时队列和 cfs 队列,实时进程任务信息放在 实时队列中,普通进程任务信息放在 cfs 队列中。

且在 task_struct 数据结构里,有对应的策略 rt,与策略Entity,这样可相互引用

如图:

3 切换 CPU 上下文以及进程空间

读取下一个任务的task_struct,切换成下一个选中任务的上下文以及进程空间

而刚刚运行的任务的相关寄存器信息,CPU自动存储到 TSS中

TSS:

每个 进程都有一个 TSS (Task State Segement,任务状态段),这里面有所有的寄存器

CPU 运行时。

还有一个 特殊的寄存器 TR (Task Register,任务寄存器),指向某个进程的 TSS,更改 TR的值时,将会触发硬件保存CPU所有寄存器的值到当前进程的 TSS中,然后从新进程的 TSS中读出所有的寄存器,加载到CPU对应的寄存器中

4 真正睡着

进入调度队列中,之前的任务没有了 CPU执行,只是变成一个 数据结构

5 等待调度,被唤醒

沉睡的任务等待被调度策略选中,读取 它的TSS 信息,然后获得CPU去运行,且从 schedule() 方法返回

其实也可以理解成,回到了 步骤 2 从队列中选出继任者进程

二 主动引发的挂起

主动调用操作系统提供的挂起的方法 - scheduler()

如:IO 操作的时候,让出 CPU

上一节文章提到的 所有挂起的方法,如 park(),sleep() 等

三 被动引发的挂起

被动挂起场景

1 调度策略

当前 CPU 运行中的进程,运行的时间太长,被 定时的 tick函数检测到需要休息

2 有优先级更高的任务

刚刚被唤醒的进程任务,优先级比当前运行的任务优先级高,则需要被换下来

注意⚠️:被动引发的挂起,并不立即执行,而是先打上 rescheduler 标签,等待时机执行 scheduler()函数

等待时机

1 从内核态返回 用户态时

2 从中断返回时

总结

一个简单的线程休息场景,在操作系统层面居然有这么多复杂的设计,细细咀嚼,别有一番风味,bingo!

附上一张简单的总结图:

相关推荐
猎人everest2 小时前
SpringBoot应用开发入门
java·spring boot·后端
孤雪心殇7 小时前
简单易懂,解析Go语言中的Map
开发语言·数据结构·后端·golang·go
小突突突9 小时前
模拟实现Java中的计时器
java·开发语言·后端·java-ee
web137656076439 小时前
Scala的宝藏库:探索常用的第三方库及其应用
开发语言·后端·scala
闲猫10 小时前
go 反射 interface{} 判断类型 获取值 设置值 指针才可以设置值
开发语言·后端·golang·反射
LUCIAZZZ10 小时前
EasyExcel快速入门
java·数据库·后端·mysql·spring·spring cloud·easyexcel
Asthenia041211 小时前
依托IOC容器提供的Bean生命周期,我们能在Bean中做些什么?又能测些什么?
后端
别说我什么都不会11 小时前
鸿蒙轻内核M核源码分析系列十二 事件Event
操作系统·harmonyos
Ase5gqe11 小时前
Spring中的IOC详解
java·后端·spring
小万编程11 小时前
基于SpringBoot+Vue奖学金评比系统(高质量源码,可定制,提供文档,免费部署到本地)
java·spring boot·后端·毕业设计·计算机毕业设计·项目源码