batch批处理
branch ch2
批处理
顺次执行程序
特权级
用户态U-Mode内核态S-Mode隔离
user/bin
有几个应用
hello_world:
在屏幕上打印一行 Hello, world!
bad_address:
访问一个非法的物理地址,测试批处理系统是否会被该错误影响
power:
不断在计算操作和打印字符串操作之间切换
应用中
user的crate名是user_lib
#[macro_use]
extern crate user_lib;
user/lib.rs
定义_start 3#[linkage = "weak"]
main是weak
因而bin和这里虽然都有main
但bin的会覆盖掉这个
封装了一下syscall 装成标准库
user/syscall
ABI 或者系统调用
应用运行用户态U
ecall
调用批处理系统提供的接口
触发Environment call from U-mode 的异常
Trap 进入 S 模式 执行批处理系统针对这个exception提供的服务程序
用俩
sys_write
sys_exit
syscall
rust内联汇编
符合riscv要求
以寄存器 a0~a2/x10-x12 来保存系统调用的参数
寄存器 a7/x17 保存 syscall ID
返回值通过寄存器 a0/x10 传递给局部变量 ret
os/src/link_app.S
cargo build时,os/build.rs生成
第一个.section .data是一个64bit整数数组
align 3 8bytes/64bit对齐
quad表示8 bytes/64bit
写入了数组长度3
和3个app的起始地址
第二个.section .data插入app们的bin
os/src/batch.rs
AppManager
app管理
lazy_static定义延迟初始化static
UPSafeCell,只允许一个内部可变引用
找到link_app.S中提供的符号_num_app
解析app数量,和开头address
load_app
fence.i
清理i-cache
d-cache数据缓存 cpu 访存
i-cache指令缓存 cpu 取指令
这里修改了cpu取指的内存区域,需要刷新i-cache
清空app程序加载区
from_raw_parts_mut
将APP_BASE_ADDRESS转为&mut [u8] 长度APP_SIZE_LIMIT
填零
获取app的bin
from_raw_parts
从start拿到end
拷贝app.bin到target地址
dst是 APP_BASE_ADDRESS开始 长度app_src.len()
特权级切换
U特权级 User Mode app使用
S特权级 Supervisor Mode os使用
M特权级 Machine Mode sbi使用
CSR
Control and Status Register,控制与状态寄存器
U-Mode下大部分CSR不可直接操作
Trap下CSR使用
CSR 名 该 CSR 与 Trap 相关的功能
sstatus SPP 等字段给出 Trap 发生之前 CPU 处在哪个特权级(S/U)等信息
sepc 当 Trap 是一个异常的时候,记录 Trap 发生之前执行的最后一条指令的地址
scause 描述 Trap 的原因
stval 给出 Trap 附加信息
stvec 控制 Trap 处理代码的入口地址
64 位的 CSR,在中断使能interrupt enable的情况下,保存了中断处理的入口地址
MODE 位于 [1:0],长度为 2 bits;
BASE 位于 [63:2],长度为 62 bits。
MODE = 0
stvec Direct 模式,
进入 S 模式的 Trap 无论原因如何,
处理 Trap 的入口地址都是 BASE<<2 ,
CPU 会跳转到这个地方进行异常处理。
还可以设置为 Vectored 模式,
sret
CPU 按照 sstatus 的 SPP 字段设置特权级 U 或者 S ;
CPU 跳转到 sepc 指向的那条instruction,然后继续执行。
TODO
riscv
app启动,初始化用户态context,切换到用户态执行app
app发起syscall,切换到os中处理
app出错,os杀死app并加载next app
app执行结束,os加载下一个app
trap 从U进S的过程
进入trap处理之前
通过os的专用stack。保存原控制流reg状态
全局变量形式在.bss段里
.bss应该负责存储全0初始化
objdump看了一下在.rodata里
ds说是llvm干的,认为const不可变,所以.rodata
而且.rodata可以处理对齐要求,但.bss不能
Q
KernelStack
UserStack
获得stack top的address
stack向下增长(这个下有歧义,看图其实是向上,因为高地址在下面,向上就是向低地址增长)
有图three-piece,所以是x86的情况
所以stack top是data开始地址+USER_STACK_SIZE
每次sp-8
self.data.as_ptr() as usize + USER_STACK_SIZE
换stack
sp修改为get_sp返回值
硬件会
TODO
riscv内容
sstatus SPP 字段会被修改为 CPU 当前的特权级(U/S)。
sepc 被修改为 Trap 处理完成后默认会执行的下一条instruction的address。
scause/stval 分别会被修改成这次 Trap 的原因以及相关的附加信息。
stvec CPU 会跳转到stvec所设置的 Trap 处理entry address,
将当前特权级设置为 S ,
然后从Trap 处理入口地址处开始执行。
完成trap,sret
CPU 按照 sstatus 的 SPP 字段设置特权级 U 或者 S ;
CPU 跳转到 sepc 指向的那条instruction,然后继续执行。
trap上下文结构体
包含所有reg,x0~x31
+sstatus, spec
x0 硬编码0,不会变化
x4/tp 除非手动出于特殊原因使用,一般不会用,无需保存
csr部分 进入trap立即覆盖掉 scause/stval/sstatus/sepc 全部或者部分
scause/stval 在 Trap 处理的第一时间就被使用或者是在其他地方保存下来了,因此它没有被修改并造成不良影响的风险。
sstatus/sepc 在 Trap 处理的全程有意义(在 Trap 控制流最后 sret 的时候还用到了它们),而且确实会出现 Trap 嵌套的情况使得它们的值被覆盖掉。
trap保存和恢复过程
stvec::write 设置Direct mode
指向__alltraps的address
src/trop/trap.S
__alltraps
将trap context保存到os stack上
跳转到使用rust编写的trap_handler
align 2^2 4bytes对齐,risc-v特权级规范要求
交换sscratch和sp
现在sp指向os stack
sscratch指向user stack
addi sp, sp, -34*8 预分配-38*8字节stack frame
使用sd保存x1,x3,x5-x27
使用了类似循环宏SAVE_GP来保存大头
在 trap.S 开头加上 .altmacro 才能正常使用 .rept 命令
跳过x0,x4/tp,x2/sp
因为sp现在指向os stack
保存sp对于时候恢复到U-Mode没有意义
sstatus给t0/x5
sepc给t1/x6
继续保存到 os stack
sscratch保存到t2/x7 这玩意儿现在是U-Mode的sp
继续保存
sp/x2给a0/x10 根据riscv调用规范,trap_handler的第一个参数是a0/x10
trap_handler需要应用程序传来的syscall ID和对应参数
由于这些reg的value可能被修改
Q 谁修改?
所以不能让trap_handler直接reg value
因而使用stack保存的值
====指令记录====
addi rd, rs1, imm
rs1+立即数imm保存进rd
例子
addi sp, sp, -34*8
sp分配38*8字节,因为向下增长,所以-
sd rs2, offset(rs1)
将rs2 reg数据存储到指定内存地址offset(rs1)
就是rs1+offset的内存地址
例子
sd x1, 1*8(sp) x1存入sp + 8的内存地址
ld rs2, offset(rs1)
操作相反
mv rd, rs
伪指令,实际是addi实现
将rs的值复制给rd
例子
mv a0, sp
将sp/x2的值复制给a0/x10
TODO
riscv
已记录
csrr
csrrw
__restore
trap_handler返回后调用
保存在os stack上的trap context 恢复reg
通过sret回到U继续执行保存在sepc里的下一条instruction
sp在__restore作用不同的时候,有可能会改变
所以先从a0读入sp
sp目前指向os stack
ld从stack top的trap context恢复csr和reg注意顺序
回收trap context占用stack内存 addi sp, sp, 34*8
交换sscratch和sp
sp指向U-Mode stack
sscratch指向S-Mode stack
sret回到U-Mode继续运行sepc里的下一条instruction
trap/mod.rs
syscall/mod.rs
syscall/fs.rs
处理trap_handler cx原样返回,因而a0的值一直是os stack top 传入的sp的值
根据scause保存的trap原因进行处理
发现trap来自U 特权级的 Environment Call
就是syscall
cx.sepc += 4;
修改sepc+=4
因为这个是通过ecall instruction触发的syscall
ecall调用后sepc会设置为ecall instruction所在的address
为了从ecall的下一条instruction开始执行
+4
__restore在sret后会从+4的sepc开始执行
cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
a0会变化,这里的a0是cx上下文中,也就是os stack里保存的a0的值,并不是当前reg a0会发生变化
从trap context取出
syscall ID,a7/x17
syscall三个参数,a0~a2/x10-x12
传给syscall
处理其他错误--
调用sys_call 只调用
sys_write/sys_exit 比较直观
next_app
trap/context.rs
batch.rs
应用结束或出错,进入下一个
load下一个app, copy到0x80400000 load_app
跳转到应用程序入口点 0x80400000; 设置sepc
将stack切换到User-Mode 走__restore流程
恢复sscratch指向os stack
从S-Mode切换到U-Mode 设置sstatus
os stack上压入一个为启动app特殊构造的trap context
TrapContext
sstatus的SPP设置为User
sepc修改为app入口点entry
sp寄存器为设定的一个stack pointer
再复用__restore
17 __restore(KERNEL_STACK.push_context(TrapContext::app_init_context(
18 APP_BASE_ADDRESS,
19 USER_STACK.get_sp(),
20 )) as *const _ as usize);
使用push_context在os上压入一个trap context,
作为__restore的参数
因为sp <- a0将参数给到sp,在__restore里sp保存的是os stack top

Generated 2025-05-05 04:17:35 +0000
25bcfca-dirty 2025-05-04 14:47:56 +0000