[RustSBI output]
[kernel] Hello, world!
AAAAAAAAAA [1/5]
BBBBBBBBBB [1/2]
....
CCCCCCCCCC [2/3]
AAAAAAAAAA [3/5]
Test write_b OK!
[kernel] Application exited with code 0
CCCCCCCCCC [3/3]
...
[kernel] Application exited with code 0
[kernel] Panicked at src/task/mod.rs:106 All applications completed!
提纲
构建应用
└── user
├── build.py(新增:使用 build.py 构建应用使得它们占用的物理地址区间不相交)
├── Makefile(修改:使用 build.py 构建应用)
└── src (各种应用程序)
改进OS:Loader
模块加载和执行程序
├── os
│ └── src
│ ├── batch.rs(移除:功能分别拆分到 loader 和 task 两个子模块)
│ ├── config.rs(新增:保存内核的一些配置)
│ ├── loader.rs(新增:将应用加载到内存并进行管理)
│ ├── main.rs(修改:主函数进行了修改)
│ ├── syscall(修改:新增若干 syscall)
改进OS:TaskManager
模块管理/切换程序的执行
├── os
│ └── src
│ ├── task(新增:task 子模块,主要负责任务管理)
│ │ ├── context.rs(引入 Task 上下文 TaskContext)
│ │ ├── mod.rs(全局任务管理器和提供给其他模块的接口)
│ │ ├── switch.rs(将任务切换的汇编代码解释为 Rust 接口 __switch)
│ │ ├── switch.S(任务切换的汇编代码)
│ │ └── task.rs(任务控制块 TaskControlBlock 和任务状态 TaskStatus 的定义)
提纲
没有更新 应用名称有数字编号
user/src/bin/
├── 00write_a.rs # 5次显示 AAAAAAAAAA 字符串
├── 01write_b.rs # 2次显示 BBBBBBBBBB 字符串
└── 02write_c.rs # 3次显示 CCCCCCCCCC 字符串
BASE_ADDRESS
都是不同的。build.py
,为每个应用定制了各自的链接脚本
应用起始地址 = 基址 + 数字编号 * 0x20000
//00write_a.rs
fn main() -> i32 {
for i in 0..HEIGHT {
for _ in 0..WIDTH {
print!("A");
}
println!(" [{}/{}]", i + 1, HEIGHT);
yield_(); //放弃处理器
}
println!("Test write_a OK!");
0
}
const SYSCALL_YIELD: usize = 124;
const SYSCALL_YIELD: usize = 124;
pub fn sys_yield() -> isize {
syscall(SYSCALL_YIELD, [0, 0, 0])
}
pub fn yield_() -> isize {
sys_yield()
}
提纲
LibOS支持在内存中驻留多个应用,形成多道程序操作系统;
fn get_base_i(app_id: usize) -> usize {
APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT
}
let base_i = get_base_i(i);
// load app from data section to memory
let src = (app_start[i]..app_start[i + 1]);
let dst = (base_i.. base_i+src.len());
dst.copy_from_slice(src);
执行时机
执行方式
entry(i)
现在完成了支持把应用都放到内存中的LibOS。
提纲
5.2 Trap控制流切换
5.3 协作式调度
协作式多道程序:应用程序主动放弃 CPU 并切换到另一个应用继续执行,从而提高系统整体执行效率;
pub enum TaskStatus {
UnInit,
Ready,
Running,
Exited,
}
1// os/src/task/context.rs
2 pub struct TaskContext {
3 ra: usize, //函数返回地址
4 sp: usize, //task内核栈指针
5 s: [usize; 12], //属于Callee函数保存的寄存器集s0~s11
6}
1// os/src/task/context.rs
2 pub struct TaskContext {
3 ra: usize,
4 sp: usize,
5 s: [usize; 12],
6}
// os/src/trap/context.rs
pub struct TrapContext {
pub x: [usize; 32],
pub sstatus: Sstatus,
pub sepc: usize,
}
任务切换是来自两个不同应用在内核中的 Trap 控制流之间的切换
从硬件的角度来看普通控制流或异常控制流的执行过程
对于当前实践的OS,没有虚拟资源,而物理资源内容就是通用寄存器/CSR寄存器
提纲
5.1 任务切换
5.3 协作式调度
在分属不同任务的两个Trap控制流之间进行hacker级操作,即进行Trap上下文切换,从而实现任务切换。
__switch()
__switch()
之后直到它返回前的这段时间,原 Trap 控制流 A 会先被暂停并被切换出去, CPU 转而运行另一个应用在内核中的 Trap 控制流 B 。a Möbius strip has only one surface.
__switch()
从实现的角度讲, __switch()
函数和一个普通的函数之间的核心差别仅仅是它会换栈 。
__switch()
阶段[1]:在 Trap 控制流 A 调用__switch()
之前,A 的内核栈上只有 Trap 上下文和 Trap 处理函数的调用栈信息,而 B 是之前被切换出去的;
阶段 [2]:A 在 A 任务上下文空间在里面保存 CPU 当前的寄存器快照;
阶段 [3]:读取 next_task_cx_ptr 指向的 B 任务上下文,恢复 ra 寄存器、s0~s11 寄存器以及 sp 寄存器。
__switch()
才能做到一个函数跨两条控制流执行,即 通过换栈也就实现了控制流的切换 。阶段 [4]:当 CPU 执行 ret 汇编伪指令完成 __switch()
函数返回后,任务 B 可以从调用 __switch()
的位置继续向下执行。
__switch()
通过恢复 sp 寄存器换到了任务 B 的内核栈上,实现了控制流的切换,从而做到一个函数跨两条控制流执行。__switch()
的接口 1 // os/src/task/switch.rs
2
3 global_asm!(include_str!("switch.S"));
4
5 use super::TaskContext;
6
7 extern "C" {
8 pub fn __switch(
9 current_task_cx_ptr: *mut TaskContext,
10 next_task_cx_ptr: *const TaskContext
11 );
12 }
__switch()
的实现12 __switch:
13 # 阶段 [1]
14 # __switch(
15 # current_task_cx_ptr: *mut TaskContext,
16 # next_task_cx_ptr: *const TaskContext
17 # )
18 # 阶段 [2]
19 # save kernel stack of current task
20 sd sp, 8(a0)
21 # save ra & s0~s11 of current execution
22 sd ra, 0(a0)
23 .set n, 0
24 .rept 12
25 SAVE_SN %n
26 .set n, n + 1
27 .endr
__switch()
的实现28 # 阶段 [3]
29 # restore ra & s0~s11 of next execution
30 ld ra, 0(a1)
31 .set n, 0
32 .rept 12
33 LOAD_SN %n
34 .set n, n + 1
35 .endr
36 # restore kernel stack of next task
37 ld sp, 8(a1)
38 # 阶段 [4]
39 ret
提纲
5.1 任务切换
5.2 Trap控制流切换
操作系统管理控制进程运行所用的信息集合
pub struct TaskControlBlock {
pub task_status: TaskStatus,
pub task_cx: TaskContext,
}
struct TaskManagerInner {
tasks: [TaskControlBlock; MAX_APP_NUM],
current_task: usize,
}
sys_yield
和sys_exit
系统调用pub fn sys_yield() -> isize {
suspend_current_and_run_next();
0
}
pub fn sys_exit(exit_code: i32) -> ! {
println!("[kernel] Application exited with code {}", exit_code);
exit_current_and_run_next();
panic!("Unreachable in sys_exit!");
}
sys_yield
和sys_exit
系统调用// os/src/task/mod.rs
pub fn suspend_current_and_run_next() {
mark_current_suspended();
run_next_task();
}
pub fn exit_current_and_run_next() {
mark_current_exited();
run_next_task();
}
sys_yield
和sys_exit
系统调用 fn run_next_task(&self) {
......
unsafe {
__switch(
current_task_cx_ptr, //当前任务上下文
next_task_cx_ptr, //下个任务上下文
);
}
Q:如何实现?
如果能搞定,我们就实现了支持多道程序协作调度的BatchOS
提纲
BatchOS可抢占应用的执行,从而可以公平和高效地分时执行多个应用,提高系统的整体效率。
// os/src/sbi.rs
pub fn set_timer(timer: usize) {
sbi_call(SBI_SET_TIMER, timer, 0, 0);
}
// os/src/timer.rs
pub fn set_next_trigger() {
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
}
pub fn rust_main() -> ! {
trap::enable_timer_interrupt();
timer::set_next_trigger();
}
// os/src/trap/mod.rs trap_handler函数
......
match scause.cause() {
Trap::Interrupt(Interrupt::SupervisorTimer) => {
set_next_trigger();
suspend_current_and_run_next();
}
}
这样我们就实现了分时多任务的腔骨龙操作系统
sys_task_info()
锯齿螈 始初龙 腔骨龙 ![bg right:40% 70%](figs/ch3-oses.png)
锯齿螈 始初龙 腔骨龙
- J. Lyons & Co.用于商业事务处理 J. Lyons & Co.是一家成立于1884年的英国连锁餐厅,食品制造业和酒店集团。
https://baike.baidu.com/item/EDSAC/7639053 电子延迟存储自动计算器(英文:Electronic Delay Storage Automatic Calculator、EDSAC)是英国的早期计算机。1946年,英国剑桥大学数学实验室的莫里斯·威尔克斯教授和他的团队受冯·诺伊曼的First Draft of a Report on the EDVAC的启发,以EDVAC为蓝本,设计和建造EDSAC,1949年5月6日正式运行,是世界上第一台实际运行的存储程序式电子计算机。 是EDSAC在工程实施中同样遇到了困难:不是技术,而是资金缺乏。在关键时刻,威尔克斯成功地说服了伦敦一家面包公司J.Lyons&Co。.的老板投资该项目,终于使计划绝处逢生。1949年5月6日,EDSAC首次试运行成功,它从带上读人一个生成平方表的程序并执行,正确地打印出结果。作为对投资的回报,LyOHS公司取得了批量生产EDSAC的权利,这就是于1951年正式投入市场的LEO计算机(Lyons Electronic Office),这通常被认为是世界上第一个商品化的计算机型号,因此这也成了计算机发展史上的一件趣事:第一家生产出商品化计算机的厂商原先竟是面包房。Lyons公司后来成为英国著名的“国际计算机有限公司”即ICL的一部分。
锯齿螈 始初龙 腔骨龙 ![bg right:40% 70%](figs/ch3-oses.png)
锯齿螈 始初龙 腔骨龙 ![bg right:40% 70%](figs/ch3-oses.png)
锯齿螈 始初龙 腔骨龙 ![bg right:40% 70%](figs/ch3-oses.png)
锯齿螈 始初龙 腔骨龙 ![bg right:40% 70%](figs/ch3-oses.png)