【rCore】用rust从零开始写一个操作系统 开源操作系统训练营:ch2 批处理系统

需要实现什么

批处理操作系统,能够自动加载并运行了所有的用户程序(将多个程序打包到一起输入计算机;当一个程序运行结束后,计算机会自动执行下一个程序)。
用户态到内核态的切换。

拥有什么

能够实现sbi调用,实现了能够在终端输出的print宏,在qemu-system-riscv64模拟 RISC-V 64 计算机的运行的程序。

思路

为用户态提供系统调用接口:
用户库使用ecall实现系统调用 -> os内核态捕获trap并执行系统调用

批量加载用户程序并运行:
用户程序打包成可执行文件并链接入口地址 -> os启动时加载所有用户程序 -> 将程序加载到固定地址并执行 -> 执行完成调用exit后运行下一个程序 -> 全部执行完成后关闭os

本章代码树

重要更改:batch.rs、trap、user\src\syscall.rs

── os
│   ├── Cargo.toml
│   ├── Makefile (修改:构建内核之前先构建应用)
│   ├── build.rs (新增:生成 link_app.S 将应用作为一个数据段链接到内核)
│   └── src
│       ├── batch.rs(新增:实现了一个简单的批处理系统)
│       ├── console.rs
│       ├── entry.asm
│       ├── lang_items.rs
│       ├── link_app.S(构建产物,由 os/build.rs 输出)
│       ├── linker.ld
│       ├── logging.rs
│       ├── main.rs(修改:主函数中需要初始化 Trap 处理并加载和执行应用)
│       ├── sbi.rs
│       ├── sync(新增:包装了RefCell,暂时不用关心)
│       │   ├── mod.rs
│       │   └── up.rs
│       ├── syscall(新增:系统调用子模块 syscall)
│       │   ├── fs.rs(包含文件 I/O 相关的 syscall)
│       │   ├── mod.rs(提供 syscall 方法根据 syscall ID 进行分发处理)
│       │   └── process.rs(包含任务处理相关的 syscall)
│       └── trap(新增:Trap 相关子模块 trap)
│           ├── context.rs(包含 Trap 上下文 TrapContext)
│           ├── mod.rs(包含 Trap 处理入口 trap_handler)
│           └── trap.S(包含 Trap 上下文保存与恢复的汇编代码)
└── user(新增:应用测例保存在 user 目录下)
   ├── Cargo.toml
   ├── Makefile
   └── src
      ├── bin(基于用户库 user_lib 开发的应用,每个应用放在一个源文件中)
      │   ├── ...
      ├── console.rs
      ├── lang_items.rs
      ├── lib.rs(用户库 user_lib)
      ├── linker.ld(应用的链接脚本)
      └── syscall.rs(包含 syscall 方法生成实际用于系统调用的汇编指令,各个具体的 syscall 都是通过 syscall 来实现的)

实现过程

为用户态提供系统调用接口

用户库实现syscall系统调用

与前一章的sbi调用实现基本一样,使用ecall指令从用户态进入内核态。

// user\src\syscall.rs
pub fn syscall(id: usize, args: [usize; 3]) -> isize {
   
    let mut ret: isize;
    unsafe {
   
        core::arch::asm!(
            "ecall",
            inlateout("x10") args[0] => ret,
            in("x11") args[1],
            in("x12") args[2],
            in("x17") id
        );
    }
    ret
}
捕获ecall指令出发的系统调用
trap

stvec 存储处理器在发生异常或中断时跳转到的异常处理基地址(trap handler的入口地址)。

stvec的模式
Direct Mode (直接模式)
在直接模式下,当异常发生时,处理器会跳转到 stvec 寄存器中设置的地址开始执行异常处理程序。所有的异常和中断都会导致处理器跳转到这个单一的入口点,然后由异常处理程序根据异常原因码来处理不同的情况。
Vectored Mode (向量模式)
在向量模式下,stvec 寄存器中的地址是中断向量表的基地址。当异常发生时,处理器会根据异常类型的不同来计算跳转地址。每种异常类型有一个固定的偏移量,处理器会将这个偏移量加到基地址上,计算得到对应的异常处理程序的地址,并跳转到该地址执行。

Trap 处理的总体流程如下:首先通过 __alltraps 将 Trap 上下文保存在内核栈上,然后跳转到使用 Rust 编写的 trap_handler 函数完成 Trap 分发及处理。当 trap_handler 返回之后,使用 __restore 从保存在内核栈上的 Trap 上下文恢复寄存器。最后通过一条 sret 指令回到应用程序执行。

设置trap处理入口,当发生trap终端时,跳转到trap.S中的__alltraps处理,trap.S调用trap_handler。

// os/src/trap/mod.rs

global_asm!(include_str!("trap.S"));

pub fn init() {
   
    extern "C" {
    fn __alltraps(); }
    unsafe {
   
        stvec::write
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值