[rCore学习笔记 023]任务切换
导读
还是要先看官方手册.
学过DMA的同志可能比较好理解,一句话, 释放CPU总线 :
如果把应用程序执行的整个过程进行进一步分析,可以看到,当程序访问 I/O 外设或睡眠时,其实是不需要占用处理器的,于是我们可以把应用程序在不同时间段的执行过程分为两类,占用处理器执行有效任务的计算阶段和不必占用处理器的等待阶段。这些阶段就形成了一个我们熟悉的“暂停-继续…”组合的控制流或执行历史。从应用程序开始执行到结束的整个控制流就是应用程序的整个执行过程。
本节的重点是操作系统的核心机制—— 任务切换 ,在内核中这种机制是在 __switch 函数中实现的。 任务切换支持的场景是:一个应用在运行途中便会主动或被动交出 CPU 的使用权,此时它只能暂停执行,等到内核重新给它分配处理器资源之后才能恢复并继续执行。
任务概念的形成
这里直接看官方手册.
这里主要是提到了一些概念,我把它们摘抄出来:
- 执行片段称为 “ 计算任务片 ”
- 空闲片段称为“ 空闲任务片 ”
- 需要保存与恢复的资源称为“ 任务上下文 ”
不同类型的上下文与切换
这部分之前第二章复习第一章的知识的时候我们就重复过关于第一章的 函数调用栈 和第二章的 内核栈/用户栈 的类比和区别.
这里直接看官方手册回顾一下就行,应该是为 任务切换 打基础.
在我的脑海里,任务切换是一个直接用sp指针进行操作的过程,按照我们上一章学到的知识,只需要在切换之前把 上下文 保存到用户栈就行.可能会增加的功能:
- 给用户栈增加入栈功能
- 增加切换APP的函数,可能需要调用汇编代码,有点类似于
__restore,但是不需要触发Trap.
任务切换的设计与实现
官方手册中提到的异同和我自己脑子里总结的异同还是非常不同的:
- 与 Trap 切换不同,它不涉及特权级切换;
- 与 Trap 切换不同,它的一部分是由 编译器帮忙 完成的;
- 与 Trap 切换相同,它对应用是 透明 的。
这个 编译器帮忙 和 对应用透明 是需要在后续学习过程中注意的.
任务切换的流程:
- 某个应用Trap到 S模式 的操作系统内核中.
- Trap控制流调用
__switch. - Trap 控制流 A 会先被暂停并被切换出去.
- CPU 转而运行另一个应用在内核中的 Trap 控制流 B
- 然后在某个合适的时机,原 Trap 控制流 A 才会从某一条 Trap 控制流 C (很有可能不是它之前切换到的 B )切换回来继续执行并最终返回
问题:既然不需要特权级切换,那它为什么还要进入Trap呢?是怎么进行的Trap吗?还是通过ecall吗?
从实现的角度讲, __switch 函数和一个普通的函数之间的核心差别仅仅是它会 换栈 。
说起栈的上下文切换,我们不得不想到上一章我们保存的包含CSR和X0~X31的上下文,那么同样地,在 任务切换 的过程中也有任务的上下文:

认真看这个图,左侧写得是 运行 状态的一个任务,它的内核栈里保存了两部分的东西:
- 上一章我们学到的
TrapContext - 那么当
Trap之后把sp指针指向内核栈,函数调用的一些上下文也会保存在内核里,除了TrapContext内核栈里还保存着TrapHandler函数的 调用栈信息 .
右侧写得是 准备 状态的一个任务(可以看到一个细节 sp 寄存器 没有指向 这个栈).
为了保证sp重新指向右侧的内核栈的时候能够 恢复现场 , 因此一定有一些东西是需要保存的,那么它就是任务上下文.
这里定义 任务上下文 : CPU 当前的某些寄存器.
可以看到左侧和右侧的图的下面都有一个TASK_MANAGER,它是一个类似于我们上一章实现的APP_MANAGER的东西,是一个结构体TaskManager的一个 全局实例 .
可以看到它保存了sp,ra,s0~s11等寄存器. 为什么 这些寄存器要保存才能 保证 任务能够继续运行,是我们接下去学习的重点.
对于TaskManager的具体实现官方手册提供了思路和细节,
- 实现一个
TaskControlBlock结构体,用于储存任务上下文TaskContext. - 为
TaskManager实现一个TaskControlBlock数组,用于储存多个上下文.
对于当前正在执行的任务的 Trap 控制流,我们用一个名为 current_task_cx_ptr 的变量来保存放置当前任务上下文的地址;而用 next_task_cx_ptr 的变量来保存放置下一个要执行任务的上下文的地址.
这里直接看示意图,可以看到实现了一个以 current_task_cx_ptr 和 next_task_cx_ptr 为参数的swtich函数用以切换上下文.
这里也说明了一件事,就是控制流本身在进行切换之前就可以感知到:
- 当前执行的是哪个任务
- 接下去要执行的是哪个任务

官方手册为我们描述了任务切换的四个阶段:
- 阶段 [1]:在 Trap 控制流 A 调用
__switch之前,A 的内核栈上只有 Trap 上下文和 Trap 处理函数的调用栈信息,而 B 是之前被切换出去的; - 阶段 [2]:A 在 A 任务上下文空间在里面保存 CPU 当前的寄存器快照;
- 阶段 [3]:这一步极为关键,读取
next_task_cx_ptr指向的 B 任务上下文,根据 B 任务上下文保存的内容来恢复ra寄存器、s0~s11寄存器以及sp寄存器。只有这一步做完后,__switch才能做到一个函数跨两条控制流执行,即 通过换栈也就实现了控制流的切换 。 - 阶段 [4]:上一步寄存器恢复完成后,可以看到通过恢复
sp寄存器换到了任务 B 的内核栈上,进而实现了控制流的切换。这就是为什么__switch能做到一个函数跨两条控制流执行。此后,当 CPU 执行ret汇编伪指令完成__switch函数返回后,任务 B 可以从调用__switch的位置继续向下执行。
这里我们可以直接看__switch的具体实现:
# os/src/task/switch.S
.altmacro
.macro SAVE_SN n
sd s\n, (\n+2)*8(a0)
.endm
.macro LOAD_SN n
ld s\n, (\n+2)*8(a1)
.endm
.section .text
.globl __switch
__switch:
# 阶段 [1]
# __switch(
# current_task_cx_ptr: *mut TaskContext,
# next_task_cx_ptr: *const TaskContext
# )
# 阶段 [2]
# save kernel stack of current task
sd sp, 8(a0)
# save ra & s0~s11 of current execution
sd ra, 0(a0)
.set n, 0
.rept 12
SAVE_SN %n
.set n, n + 1
.endr
# 阶段 [3]
# restore ra & s0~s11 of next execution
ld ra, 0(a1)
.set n, 0
.rept 12
LOAD_SN %n
.set n, n + 1
.endr
# restore kernel stack of next task
ld sp, 8(a1)
# 阶段 [4]
ret
这里应该没什么看不懂的部分,我画了一张图来表述TaskContext的内存情况:

对应rust的代码:
// os/src/task/context.rs
pub struct TaskContext {
ra: usize,
sp: usize,
s: [usize; 12],
}
这里提一下:
- 在RISC-V架构中,
ra寄存器(Return Address Register)是一个特殊的通用寄存器,编号为x1。这个寄存器主要用于存储返回地址,即函数调用之后应该返回的指令地址。当一个函数被调用时,调用者(caller)通常会将返回地址存入 ra寄存器 ,以便在函数执行完毕后能够正确返回到调用点。在__swtich执行结束后使用ret返回到ra的位置,我们修改了ra为下一个要执行的任务上下文的ra自然会继续执行到执行的任务上次保存上下文时调用__swtich的位置 . - Rust/C 编译器会在函数的起始位置自动生成代码来保存
s0~s11这些被调用者保存的寄存器。但__switch是一个用汇编代码写的特殊函数,它不会被 Rust/C 编译器处理,所以我们需要在__switch中手动编写保存s0~s11的汇编代码. - 不用保存其它寄存器是因为:其它寄存器中,属于调用者保存的寄存器是由编译器在 高级语言 编写的调用函数中 自动生成 的代码来完成保存的;还有一些寄存器属于临时寄存器,不需要保存和恢复。
对应 第三点 ,我们应该理解,要使用Rust调用才能使得编译器自动帮我们 保存/恢复调用者保存寄存器 :
// os/src/task/switch.rs
global_asm!(include_str!("switch.S"));
use super::TaskContext;
extern "C" {
pub fn __switch(
current_task_cx_ptr: *mut TaskContext,
next_task_cx_ptr: *const TaskContext
);
}
[rCore学习笔记 023]任务切换的更多相关文章
- Cocos2dx 学习笔记整理----场景切换
据说Cocos2dx场景切换的方法有32种:cocos2dx 常见的32种切换场景的动画 无需一一求证,只需要知道切换场景需要怎么做就行了. 作为导演CCDirector,切换场景的事情当然归它管了. ...
- Android学习笔记 ImageSwitcher图片切换组件的使用
activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...
- iOS学习笔记04-视图切换
一.视图切换 UITabBarController (分页控制器) - 平行管理视图 UINavigationController (导航控制器) - 压栈出栈管理视图 模态窗口 二.UITabBar ...
- 【原】Java学习笔记023 - 字符串缓冲区_正则表达式
package cn.temptation; import java.util.Arrays; public class Sample01 { public static void main(Stri ...
- Android学习笔记 TextSwitcher文本切换组件的使用
activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android&qu ...
- Linux 学习笔记
Linux学习笔记 请切换web视图查看,表格比较大,方法:视图>>web板式视图 博客园不能粘贴图片吗 http://wenku.baidu.com/view/bda1c3067fd53 ...
- 《Linux内核分析》第八周学习笔记
<Linux内核分析>第八周学习笔记 进程的切换和系统的一般执行过程 郭垚 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163 ...
- [原创]java WEB学习笔记69:Struts2 学习之路-- 消息处理与国际化,概述,配置国际资源文件,访问国际化消息,通过超链接切换语言
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
- 【Unity 3D】学习笔记三十五:游戏实例——摄像机切换镜头
摄像机切换镜头 在游戏中常常会切换摄像机来观察某一个游戏对象,能够说.在3D游戏开发中,摄像头的切换是不可或缺的. 这次我们学习总结下摄像机怎么切换镜头. 代码: private var Camera ...
- 【转】 Pro Android学习笔记(四二):Fragment(7):切换效果
目录(?)[-] 利用setTransition 利用setCustomAnimations 通过ObjectAnimator自定义动态效果 程序代码的编写 利用fragment transactio ...
随机推荐
- Thread.sleep 延时查询或延时查询前更新es缓存数据
Thread.sleep 延时查询或延时查询前更新es缓存数据 MQ消息的顺序性,或发送MQ的发送端未严格事务处理,可能存在数据未落库的情况,而导致接收端处理MQ消息的时候,查询为空. //demo1 ...
- windows系统安装或使用inspect.exe工具
确认是否安装? 结合工具everything,进行搜索 选择对应操作系统的版本,右键->选择打开路径,进到inspect.exe的安装路径,双击打开软件 软件开启后,就会自动开始抓取目前软件界面 ...
- 前端模拟接口工具推荐—Apifox(mock数据)
参考文章:https://blog.csdn.net/m0_67403272/article/details/123376945 高级mock部分 1.通过设置期望值,选择类型,比对body部分的参数 ...
- python之集合学习
*******************集合{set}******************* 1.集合set 可变 特点:是由不同元素组成 是无序的 集合中元素必须是不可变类型例如(字符串/元祖/数字) ...
- 红黑树详细讲解(结合JavaTreeMap)
1:红黑树简介 红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性.同时红黑树更是一颗自平衡的排序二叉树.根据二叉查找树的概念可以得出正常情况下查找的时间复杂度为O(log n),但是 ...
- HTTP协议 学习:2-基于libcurl的开发
HTTP协议 学习:2-基于libcurl的开发 背景 上一讲我们介绍了HTTP报文的一些内容,这一讲我们基于http有关的开源库,进行HTTP通信.最后再完成一个简单的下载小程序. ref : ht ...
- 记一次win10 python -m http.server 启动后无法访问的经历
前言 最近需要在win10上使用python创建一个http文件服务(默认端口 8000),结果执行了 python3 -m http.server -b 0.0.0.0 后,发现服务跑起来了,但浏览 ...
- OtterCTF 2018 Forensics
OtterCTF 2018 Forensics 题单来自NSSCTF [OtterCTF 2018]What the password? [OtterCTF 2018]General Info [Ot ...
- Yaml配置文件语法详解
YAML 简介 YAML,即 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写,YAML 意思其实是" Yet Ano ...
- mac idea 更换主题
使用 主题一 xcode-dark-theme:点我直达 主题二 one-dark-theme:点我直达 主题三 dark-purple-theme:点我直达 主题四(推荐) vuesion-them ...