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

背景

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

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

一 进程挂起具体执行逻辑

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

线程挂起的时候,其中核心逻辑为-用户态主动执行操作系统提供的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!

附上一张简单的总结图:

相关推荐
hlsd#25 分钟前
go mod 依赖管理
开发语言·后端·golang
陈大爷(有低保)29 分钟前
三层架构和MVC以及它们的融合
后端·mvc
亦世凡华、29 分钟前
【启程Golang之旅】从零开始构建可扩展的微服务架构
开发语言·经验分享·后端·golang
河西石头30 分钟前
一步一步从asp.net core mvc中访问asp.net core WebApi
后端·asp.net·mvc·.net core访问api·httpclient的使用
2401_8574396942 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧66643 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
阿华的代码王国1 小时前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
小码编匠1 小时前
领域驱动设计(DDD)要点及C#示例
后端·c#·领域驱动设计
skaiuijing2 小时前
Sparrow系列拓展篇:对调度层进行抽象并引入IPC机制信号量
c语言·算法·操作系统·调度算法·操作系统内核
德育处主任Pro2 小时前
『Django』APIView基于类的用法
后端·python·django