Skip to content

rcore-os/tg-rcore-tutorial-ch1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

57 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

第一章:应用程序与基本执行环境

本章实现了一个最简单的 RISC-V S 态裸机程序(tg-rcore-tutorial-ch1),展示操作系统的最小执行环境。程序在 QEMU 模拟的 RISC-V 64 硬件上运行,不依赖 OpenSBI 或 RustSBI,通过 -bios none 模式直接启动,打印 Hello, world! 后关机。

通过本章的学习和实践,你将理解:

  • 应用程序的执行环境是什么,为什么 Hello, world! 并不简单
  • 如何让 Rust 程序脱离标准库,在裸机上运行
  • RISC-V 的启动流程和特权级机制
  • SBI 的作用以及操作系统如何与硬件交互

练习任务(以教代学,学以致用):

  • 学:读本文件,了解相关OS知识,在某个开发环境(在线或本地)中正确编译运行rcore-tutorial-ch1
  • 教:分析并改进rcore-tutorial-ch1的文档和代码,让自己更高效地完成本章学习。
  • 用:基于rcore-tutorial-ch1的源代码,用gpu framebuffer 显示以代码中的数组表示的七巧板图形信息,形成七巧板构成的“O”和“S”图案。demo

注:与AI充分合作,并保存与AI合作的交互过程,总结如何做到与AI合作提升自己的操作系统知识与能力。

项目结构

tg-rcore-tutorial-ch1/
├── .cargo/
│   └── config.toml     # Cargo 配置:指定交叉编译目标和 QEMU runner
├── build.rs            # 构建脚本:自动生成链接脚本
├── Cargo.toml          # 项目配置与依赖
├── README.md           # 本文档
└── src/
    └── main.rs         # 程序源码:入口、主函数、panic 处理

源码阅读导航索引

返回根文档导航总表

建议把本章源码阅读聚焦在一个文件:src/main.rs

阅读顺序 位置 重点问题
1 _start 为什么裸机入口要手动设栈,且不能依赖标准运行时?
2 rust_main 最小执行环境中,console_putcharshutdown 如何构成完整闭环?
3 panic_handler #![no_std] 下发生异常时,系统如何收口与退出?

配套建议:阅读 tg-rcore-tutorial-sbi/src/lib.rs 中的 SBI 调用封装,理解 console_putchar/shutdown 的底层调用路径。

DoD 验收标准(本章完成判据)

  • 能在 tg-rcore-tutorial-ch1 目录执行 cargo run,看到 Hello, world! 并正常关机退出
  • 能解释 #![no_std]#![no_main] 在裸机实验中的必要性
  • 能从 src/main.rs 说明 _start -> rust_main -> panic_handler 的控制流
  • 能说明 tg-rcore-tutorial-sbi 在本章承担的最小职责(输出字符与关机)

概念-源码-测试三联表

核心概念 源码入口 自测方式(命令/现象)
裸机入口与手动设栈 tg-rcore-tutorial-ch1/src/main.rs_start cargo run 可启动且无运行时依赖报错
SBI 最小服务调用 tg-rcore-tutorial-ch1/src/main.rsrust_maintg-rcore-tutorial-sbi/src/lib.rs 看到串口输出后正常关机
无标准库异常处理 tg-rcore-tutorial-ch1/src/main.rspanic_handler 人为触发 panic 时可打印信息并异常关机

遇到构建/运行异常可先查看根文档的“高频错误速查表”。

一、环境准备

1.1 安装 Rust 工具链

本项目使用 Rust 语言编写,需要通过 rustup 安装 Rust 工具链。

Linux / macOS / WSL:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"

Windows:

https://rustup.rs 下载并运行 rustup-init.exe

验证安装:

rustc --version    # 应显示 rustc 1.xx.x
cargo --version    # 应显示 cargo 1.xx.x

1.2 添加 RISC-V 64 编译目标

由于 tg-rcore-tutorial-ch1 是面向 RISC-V 64 裸机平台的程序,需要添加对应的编译目标:

rustup target add riscv64gc-unknown-none-elf

这个目标三元组的含义是:

  • riscv64gc:RISC-V 64 位,支持 G(通用)和 C(压缩)指令集扩展
  • unknown:没有特定的 CPU 厂商
  • none:没有操作系统
  • elf:生成 ELF 格式的可执行文件,无标准运行时库

1.3 安装 QEMU 模拟器

tg-rcore-tutorial-ch1 在 QEMU 模拟的 RISC-V 64 虚拟机上运行,需要安装 qemu-system-riscv64(建议版本 >= 7.0)。

Ubuntu / Debian:

sudo apt update
sudo apt install qemu-system-misc

macOS(Homebrew):

brew install qemu

验证安装:

qemu-system-riscv64 --version

1.4 获取源代码

方式一 只获取本实验

cargo clone tg-rcore-tutorial-ch1
cd tg-rcore-tutorial-ch1

获取所有8个实验和所依赖的tg-* crates. 方式二

git clone https://github.com/rcore-os/tg-rcore-tutorial.git
cd tg-rcore-tutorial/ch1

二、编译与运行

2.1 编译

tg-rcore-tutorial-ch1 目录下执行:

cargo build

这条命令实际上执行的是交叉编译——编译器在你的主机(如 x86_64)上运行,但生成的可执行文件是针对 riscv64gc-unknown-none-elf 平台的。这个目标平台由 .cargo/config.toml 中的配置自动指定:

[build]
target = "riscv64gc-unknown-none-elf"

编译过程中,build.rs 构建脚本会自动检测目标架构,为 RISC-V 64 生成链接脚本(linker.ld),控制程序的内存布局。

编译成功后,可执行文件位于 target/riscv64gc-unknown-none-elf/debug/tg-rcore-tutorial-ch1

2.2 运行

cargo run

cargo run 在编译成功后会自动调用 .cargo/config.toml 中配置的 runner 来执行程序。实际执行的命令等价于:

qemu-system-riscv64 \
    -machine virt \
    -nographic \
    -bios none \
    -kernel target/riscv64gc-unknown-none-elf/debug/tg-rcore-tutorial-ch1

QEMU 参数说明:

参数 说明
-machine virt 使用 QEMU 的 virt 虚拟平台,这是一个通用的 RISC-V 虚拟机
-nographic 无图形界面,所有输出通过串口重定向到终端
-bios none 不加载任何 BIOS/SBI 固件,tg-rcore-tutorial-ch1 自带 M-mode 启动代码
-kernel <文件> 将 ELF 可执行文件加载到内存中作为内核启动

2.3 预期输出

Hello, world!

输出一行 Hello, world! 后,QEMU 自动退出。这是因为程序通过 SBI 调用执行了关机操作。


三、操作系统核心概念

以下内容帮助你理解 tg-rcore-tutorial-ch1 代码背后的操作系统原理。

3.1 应用程序执行环境

大多数程序员的职业生涯都从 Hello, world! 开始。然而,要在屏幕上打印一行字,并不像表面上那么简单。

在日常开发中,我们编写的应用程序运行在一个多层次的执行环境栈之上:

  ┌─────────────────────────┐
  │      应用程序            │  ← 你写的代码
  ├─────────────────────────┤
  │   标准库 (std / libc)    │  ← println! 等函数的实现
  ├─────────────────────────┤
  │     操作系统内核         │  ← 系统调用:write, exit 等
  ├─────────────────────────┤
  │   硬件抽象层 (SBI/BIOS)  │  ← 固件,为内核提供基础服务
  ├─────────────────────────┤
  │       硬件 (CPU/内存)    │  ← 物理硬件
  └─────────────────────────┘

每一层为上一层提供服务,层与层之间通过明确定义的接口交互:

  • 应用程序通过系统调用(如 ecall)请求操作系统服务
  • 操作系统通过 SBI 调用请求固件服务
  • 固件直接操作硬件

当我们在 Linux 上执行 println!("Hello, world!") 时,实际经历了:println! → Rust 标准库 → libc 的 write() → Linux 内核 sys_write 系统调用 → 串口/终端驱动 → 硬件显示。

tg-rcore-tutorial-ch1 做了什么? 它跳过了标准库和操作系统内核,直接在裸机上通过 SBI 接口输出字符。这就是"最小执行环境"的含义。

3.2 移除标准库依赖

要让程序在裸机上运行,首先需要摆脱对操作系统的依赖。Rust 标准库 std 依赖操作系统提供的系统调用(如文件 I/O、内存分配、线程等),在没有操作系统的裸机上无法使用。

tg-rcore-tutorial-ch1 在 src/main.rs 的开头使用了两个关键的属性标记:

#![no_std] —— 不使用标准库

告诉 Rust 编译器不链接标准库 std,改用核心库 core。核心库 core 是 Rust 语言的子集实现,不依赖任何操作系统功能,包含了基本类型、迭代器、Option/Result 等核心机制。

#![no_main] —— 不使用标准入口

标准的 main() 函数入口需要运行时环境(如 C runtime)进行初始化。在裸机环境中没有这些支持,所以我们告诉编译器不使用标准入口,自己定义程序的入口点 _start

#[panic_handler] —— 自定义 panic 处理

标准库提供了 panic 时打印错误信息并终止程序的功能。使用 #![no_std] 后,需要自己实现 panic 处理函数。tg-rcore-tutorial-ch1 中的实现是直接调用 SBI 关机:

#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    shutdown(true)  // 以异常状态关机
}

什么是交叉编译?

编译器运行在主机平台(如 x86_64-unknown-linux-gnu)上,但生成的可执行文件需要在目标平台(riscv64gc-unknown-none-elf)上运行,这种情况称为交叉编译(Cross Compile)。.cargo/config.toml 中的 target = "riscv64gc-unknown-none-elf" 配置使 cargo 自动进行交叉编译。

3.3 裸机启动流程

理解程序如何在裸机上启动,是操作系统学习的重要一步。

tg-rcore-tutorial-ch1 采用 nobios 模式-bios none),不依赖外部 SBI 固件,而是在 tg-rcore-tutorial-sbi 库中自带了一个最小的 M-mode 启动代码。启动流程如下:

QEMU 加电
    │
    ▼
PC = 0x1000(QEMU 内置引导代码)
    │
    ▼
跳转到 0x80000000(M-mode 入口,tg-rcore-tutorial-sbi 的 _m_start)
    │  ── 在 M-mode 下初始化硬件环境
    │  ── 设置中断委托、PMP 等
    ▼
跳转到 0x80200000(S-mode 入口,tg-rcore-tutorial-ch1 的 _start)
    │  ── 设置栈指针 sp
    ▼
跳转到 rust_main()
    │  ── 打印 "Hello, world!"
    │  ── 调用 SBI shutdown 关机
    ▼
QEMU 退出

关键地址:

  • 0x80000000:M-mode 代码的起始地址,由链接脚本中的 M_BASE_ADDRESS 指定
  • 0x80200000:S-mode 代码的起始地址,由链接脚本中的 S_BASE_ADDRESS 指定,这是 _start 函数所在的位置

链接脚本的作用

链接脚本控制程序各段在内存中的布局。build.rs 在编译时自动生成链接脚本,将程序组织为:

地址空间布局:

0x80000000  ┌────────────────────┐
            │  .text.m_entry     │  M-mode 入口代码(tg-rcore-tutorial-sbi)
            │  .text.m_trap      │  M-mode 中断处理
            │  .bss.m_stack      │  M-mode 栈空间
            │  .bss.m_data       │  M-mode 数据
            │        ...         │
0x80200000  ├────────────────────┤
            │  .text             │  S-mode 代码段(含 .text.entry)
            │  .rodata           │  只读数据段
            │  .data             │  可读写数据段
            │  .bss              │  未初始化数据段(含栈)
            └────────────────────┘

栈空间初始化

在裸机环境中,没有操作系统帮我们设置栈。_start 是一个裸函数#[unsafe(naked)]),它不会生成函数序言(prologue)和尾声(epilogue),可以在没有栈的情况下执行。它做的第一件事就是设置栈指针 sp,然后跳转到 Rust 函数 rust_main

#[unsafe(naked)]
#[unsafe(no_mangle)]
#[unsafe(link_section = ".text.entry")]
unsafe extern "C" fn _start() -> ! {
    const STACK_SIZE: usize = 4096;

    #[unsafe(link_section = ".bss.uninit")]
    static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE];

    core::arch::naked_asm!(
        "la sp, {stack} + {stack_size}",  // 将 sp 设置为栈顶地址
        "j  {main}",                       // 跳转到 rust_main
        stack_size = const STACK_SIZE,
        stack      =   sym STACK,
        main       =   sym rust_main,
    )
}

注意:Rust edition 2024 要求 no_manglelink_section 等 unsafe 属性必须用 unsafe(...) 包装,这与 edition 2021 的写法不同。

栈大小为 4096 字节(4 KiB),放置在 .bss.uninit 段中。la sp, STACK + 4096sp 设置为栈顶地址(栈从高地址向低地址增长)。

3.4 SBI 与特权级

RISC-V 特权级

RISC-V 定义了三个特权级(Privilege Level),从高到低:

特权级 缩写 说明
Machine Mode M-mode 最高特权级,直接访问所有硬件资源
Supervisor Mode S-mode 操作系统内核运行的特权级
User Mode U-mode 应用程序运行的特权级

不同特权级之间通过 ecall(Environment Call)指令切换:

  • 应用程序(U-mode)执行 ecall → 陷入操作系统(S-mode):这是系统调用
  • 操作系统(S-mode)执行 ecall → 陷入固件(M-mode):这是 SBI 调用

虽然都是 ecall 指令,但因为所在特权级不同,产生的效果也不同。

SBI(Supervisor Binary Interface)

SBI 是 RISC-V 的标准规范,定义了 S-mode 软件(操作系统)向 M-mode 固件请求服务的接口。可以把 SBI 理解为"操作系统的操作系统"——它为操作系统提供最基本的硬件抽象服务。

tg-rcore-tutorial-ch1 通过 use tg_sbi::{console_putchar, shutdown} 引入了两个 SBI 服务:

函数 说明
console_putchar(c) 向控制台输出一个字符(通过串口)
shutdown(fail) 关闭虚拟机(fail=false 正常关机,fail=true 异常关机)

rust_main 的实现非常简洁——逐字符输出 "Hello, world!\n",然后关机:

extern "C" fn rust_main() -> ! {
    for c in b"Hello, world!\n" {
        console_putchar(*c);
    }
    shutdown(false) // false 表示正常关机
}

nobios 模式的特殊之处

传统方案(如 rCore-Tutorial 旧版)使用外部 SBI 固件(如 RustSBI),需要将 SBI 固件和内核分别加载。tg-rcore-tutorial-ch1 采用 tg-rcore-tutorial-sbinobios 特性,将 M-mode 启动代码直接编译进同一个 ELF 文件中,因此可以用 -bios none -kernel 的方式一步加载,简化了启动流程。


四、代码解读

4.1 .cargo/config.toml —— 交叉编译与运行配置

[build]
target = "riscv64gc-unknown-none-elf"

[target.riscv64gc-unknown-none-elf]
runner = [
    "qemu-system-riscv64",
    "-machine", "virt",
    "-nographic",
    "-bios", "none",
    "-kernel",
]
  • [build] target:设置默认编译目标为 RISC-V 64 裸机平台,每次 cargo build 自动交叉编译
  • [target...] runner:设置运行器为 QEMU,cargo run 时自动在 QEMU 中执行编译产物

4.2 Cargo.toml —— 项目配置

[package]
name = "tg-rcore-tutorial-ch1"
edition = "2024"
# ...

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[dependencies]
tg-rcore-tutorial-sbi = { version = "0.1.0-preview.1", features = ["nobios"] }

关键配置:

  • edition = "2024":使用 Rust 2024 edition,要求 unsafe 属性使用 unsafe(...) 包装
  • panic = "abort":panic 时直接终止,不进行栈展开(unwinding),减少裸机程序的复杂度
  • tg-rcore-tutorial-sbi 依赖启用了 nobios 特性,使其内建 M-mode 启动代码

4.3 build.rs —— 构建脚本

fn main() {
    use std::{env, fs, path::PathBuf};

    if env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default() == "riscv64" {
        let ld = PathBuf::from(env::var_os("OUT_DIR").unwrap()).join("linker.ld");
        fs::write(&ld, LINKER_SCRIPT).unwrap();
        println!("cargo:rustc-link-arg=-T{}", ld.display());
    }
}

构建脚本在编译之前自动执行:

  1. 检测目标架构是否为 riscv64
  2. 如果是,将内嵌的链接脚本写入 OUT_DIR/linker.ld
  3. 通过 cargo:rustc-link-arg 指示链接器使用该脚本

链接脚本定义了两个关键地址:

  • M_BASE_ADDRESS = 0x80000000:M-mode 代码起始地址
  • S_BASE_ADDRESS = 0x80200000:S-mode 代码起始地址(_start 所在位置)

4.4 src/main.rs —— 程序源码

整个程序由五部分组成:

模块文档与属性标记(第 1-19 行):

  • 模块级文档注释(//!)概述本章关键概念
  • #![no_std]:不使用标准库
  • #![no_main]:不使用标准入口
  • cfg_attr:在 RISC-V 64 上启用严格警告,其他架构允许死代码(用于 cargo publish --dry-run 在主机上通过编译)

SBI 引入(第 21-23 行):

  • use tg_sbi::{console_putchar, shutdown} 明确引入所需的两个 SBI 函数

入口函数 _start(第 34-53 行):

  • 仅在 riscv64 架构下编译(#[cfg(target_arch = "riscv64")]
  • 裸函数,放置在 .text.entry 段,链接脚本将其安排在 0x80200000
  • edition 2024 要求使用 #[unsafe(no_mangle)]#[unsafe(link_section = "...")] 语法
  • 分配 4 KiB 栈空间,设置 sp 后跳转到 rust_main

主函数 rust_main(第 59-64 行):

  • 逐字节调用 console_putchar 输出 "Hello, world!\n"
  • 调用 shutdown(false) 正常关机

panic 处理(第 69-72 行):

  • 发生 panic 时调用 shutdown(true) 以异常方式关机

非 RISC-V 占位模块 stub(第 78-95 行):

  • 提供 main__libc_start_main 等符号,使得在非 RISC-V 平台上也能通过编译(用于 cargo publish --dry-run 验证)

五、本章小结

通过本章的学习和实践,你完成了从普通应用程序到裸机程序的蜕变过程:

  1. 理解了执行环境:应用程序依赖多层执行环境(标准库 → 操作系统 → 硬件),Hello, world! 的背后并不简单
  2. 摆脱了标准库:通过 #![no_std]#![no_main],让 Rust 程序不再依赖操作系统
  3. 掌握了裸机启动流程:从 QEMU 加电到 M-mode 初始化,再到 S-mode 的 _start 入口
  4. 认识了 RISC-V 特权级和 SBI:M-mode / S-mode / U-mode 的层次关系,以及 ecall 指令如何跨越特权级

这是操作系统内核开发的第一步——在后续章节中,我们将在这个最小执行环境的基础上,逐步添加批处理、多道程序、内存管理、进程调度等操作系统核心功能。

六、思考题

  1. 为什么 _start 函数必须是裸函数(#[naked])? 如果不是裸函数会发生什么问题?提示:思考函数序言(prologue)需要什么前提条件。

  2. ecall 指令在不同特权级中的效果有何不同? 为什么应用程序和操作系统都使用 ecall,却能产生不同的行为?

  3. 如果把链接脚本中的 S_BASE_ADDRESS0x80200000 改为其他值(如 0x80100000),程序还能正常运行吗? 需要做哪些相应的修改?

参考资料

Dependencies

依赖 说明
tg-rcore-tutorial-sbi SBI 调用封装库,支持 nobios 模式,内建 M-mode 启动代码

附录:rCore-Tutorial 组件分析表

表 1:tg-rcore-tutorial-ch1 ~ tg-rcore-tutorial-ch8 操作系统内核总体情况描述表

操作系统内核 所涉及核心知识点 主要完成功能 所依赖的组件
tg-rcore-tutorial-ch1 应用程序执行环境
裸机编程(Bare-metal)
SBI(Supervisor Binary Interface)
RISC-V 特权级(M/S-mode)
链接脚本(Linker Script)
内存布局(Memory Layout)
Panic 处理
最小 S-mode 裸机程序
QEMU 直接启动(无 OpenSBI)
打印 "Hello, world!" 并关机
演示最基本的 OS 执行环境
tg-rcore-tutorial-sbi
tg-rcore-tutorial-ch2 批处理系统(Batch Processing)
特权级切换(U-mode ↔ S-mode)
Trap 处理(ecall / 异常)
上下文保存与恢复
系统调用(write / exit)
用户态 / 内核态
sret 返回指令
批处理操作系统
顺序加载运行多个用户程序
特权级切换和 Trap 处理框架
实现 write / exit 系统调用
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-syscall
tg-rcore-tutorial-ch3 多道程序(Multiprogramming)
任务控制块(TCB)
协作式调度(yield)
抢占式调度(Preemptive)
时钟中断(Clock Interrupt)
时间片轮转(Time Slice)
任务切换(Task Switch)
任务状态(Ready/Running/Finished)
clock_gettime 系统调用
多道程序与分时多任务
多程序同时驻留内存
协作式 + 抢占式调度
时钟中断与时间管理
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-syscall
tg-rcore-tutorial-ch4 虚拟内存(Virtual Memory)
Sv39 三级页表(Page Table)
地址空间隔离(Address Space)
页表项(PTE)与标志位
地址转换(VA → PA)
异界传送门(MultislotPortal)
ELF 加载与解析
堆管理(sbrk)
恒等映射(Identity Mapping)
内存保护(Memory Protection)
satp CSR
引入 Sv39 虚拟内存
每个用户进程独立地址空间
跨地址空间上下文切换
进程隔离和内存保护
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-kernel-alloc
tg-rcore-tutorial-kernel-vm
tg-rcore-tutorial-syscall
tg-rcore-tutorial-ch5 进程(Process)
进程控制块(PCB)
进程标识符(PID)
fork(地址空间深拷贝)
exec(程序替换)
waitpid(等待子进程)
进程树 / 父子关系
初始进程(initproc)
Shell 交互式命令行
进程生命周期(Ready/Running/Zombie)
步幅调度(Stride Scheduling)
引入进程管理
fork / exec / waitpid 系统调用
动态创建、替换、等待进程
Shell 交互式命令行
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-kernel-alloc
tg-rcore-tutorial-kernel-vm
tg-rcore-tutorial-syscall
tg-rcore-tutorial-task-manage
tg-rcore-tutorial-ch6 文件系统(File System)
easy-fs 五层架构
SuperBlock / Inode / 位图
DiskInode(直接+间接索引)
目录项(DirEntry)
文件描述符表(fd_table)
文件句柄(FileHandle)
VirtIO 块设备驱动
MMIO(Memory-Mapped I/O)
块缓存(Block Cache)
硬链接(Hard Link)
open / close / read / write 系统调用
引入文件系统与 I/O
用户程序存储在磁盘镜像(fs.img)
VirtIO 块设备驱动
easy-fs 文件系统实现
文件打开 / 关闭 / 读写
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-kernel-alloc
tg-rcore-tutorial-kernel-vm
tg-rcore-tutorial-syscall
tg-rcore-tutorial-task-manage
tg-rcore-tutorial-easy-fs
tg-rcore-tutorial-ch7 进程间通信(IPC)
管道(Pipe)
环形缓冲区(Ring Buffer)
统一文件描述符(Fd 枚举)
信号(Signal)
信号集(SignalSet)
信号屏蔽字(Signal Mask)
信号处理函数(Signal Handler)
kill / sigaction / sigprocmask / sigreturn
命令行参数(argc / argv)
I/O 重定向(dup)
进程间通信-管道
异步事件通知(信号)
统一文件描述符抽象
信号发送 / 注册 / 屏蔽 / 返回
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-kernel-alloc
tg-rcore-tutorial-kernel-vm
tg-rcore-tutorial-syscall
tg-rcore-tutorial-task-manage
tg-rcore-tutorial-easy-fs
tg-rcore-tutorial-signal
tg-rcore-tutorial-signal-impl
tg-rcore-tutorial-ch8 同步互斥(Sync&Mutex)
线程(Thread)/ 线程标识符(TID)
进程-线程分离
竞态条件(Race Condition)
临界区(Critical Section)
互斥(Mutual Exclusion)
互斥锁(Mutex:自旋锁 vs 阻塞锁)
信号量(Semaphore:P/V 操作)
条件变量(Condvar)
管程(Monitor:Mesa 语义)
线程阻塞与唤醒(wait queue)
死锁(Deadlock)/ 死锁四条件
银行家算法(Banker's Algorithm)
双层管理器(PThreadManager)
进程-线程分离
同一进程内多线程并发
互斥锁(MutexBlocking)
信号量(Semaphore)
条件变量(Condvar)
线程阻塞与唤醒机制
死锁检测(练习)
tg-rcore-tutorial-sbi
tg-rcore-tutorial-linker
tg-rcore-tutorial-console
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-kernel-alloc
tg-rcore-tutorial-kernel-vm
tg-rcore-tutorial-syscall
tg-rcore-tutorial-task-manage
tg-rcore-tutorial-easy-fs
tg-rcore-tutorial-signal
tg-rcore-tutorial-signal-impl
tg-rcore-tutorial-sync

表 2:tg-rcore-tutorial-ch1 ~ tg-rcore-tutorial-ch8 操作系统内核所依赖组件总体情况描述表

功能组件 所涉及核心知识点 主要完成功能 所依赖的组件
tg-rcore-tutorial-sbi SBI(Supervisor Binary Interface)
console_putchar / console_getchar
系统关机(shutdown)
RISC-V 特权级(M/S-mode)
ecall 指令
S→M 模式的 SBI 调用封装
字符输出 / 字符读取
系统关机
支持 nobios 直接操作 UART
tg-rcore-tutorial-console 控制台 I/O
格式化输出(print! / println!)
日志系统(Log Level)
自旋锁保护的全局控制台
可定制 print! / println! 宏
log::Log 日志实现
Console trait 抽象底层输出
tg-rcore-tutorial-kernel-context 上下文(Context)
Trap 帧(Trap Frame)
寄存器保存与恢复
特权级切换
stvec / sepc / scause CSR
LocalContext(本地上下文)
ForeignContext(跨地址空间上下文)
异界传送门(MultislotPortal)
用户/内核态切换上下文管理
LocalContext 结构
ForeignContext(含 satp)
MultislotPortal 跨地址空间执行
tg-rcore-tutorial-kernel-alloc 内核堆分配器
伙伴系统(Buddy Allocation)
动态内存管理
#[global_allocator]
基于伙伴算法的 GlobalAlloc
堆初始化(init)
物理内存转移(transfer)
tg-rcore-tutorial-kernel-vm 虚拟内存管理
页表(Page Table)
Sv39 分页(三级页表)
虚拟地址(VAddr)/ 物理地址(PAddr)
虚拟页号(VPN)/ 物理页号(PPN)
页表项(PTE)/ 页表标志位(VmFlags)
地址空间(AddressSpace)
PageManager trait
地址翻译(translate)
Sv39 页表管理
AddressSpace 地址空间抽象
虚实地址转换
页面映射(map / map_extern)
页表项操作
tg-rcore-tutorial-syscall 系统调用(System Call)
系统调用号(SyscallId)
系统调用分发(handle)
系统调用结果(Done / Unsupported)
Caller 抽象
IO / Process / Scheduling / Clock /
Signal / Thread / SyncMutex trait 接口
系统调用 ID 与参数定义
trait 接口供内核实现
init_io / init_process / init_scheduling /
init_clock / init_signal /
init_thread / init_sync_mutex
支持 kernel / user feature
tg-rcore-tutorial-signal-defs
tg-rcore-tutorial-task-manage 任务管理(Task Management)
调度(Scheduling)
进程管理器(PManager, proc feature)
双层管理器(PThreadManager, thread feature)
ProcId / ThreadId
就绪队列(Ready Queue)
Manage trait / Schedule trait
进程等待(wait / waitpid)
线程等待(waittid)
阻塞与唤醒(blocked / re_enque)
Manage 和 Schedule trait 抽象
proc feature:单层进程管理器(PManager)
thread feature:双层管理器(PThreadManager)
进程树 / 父子关系
线程阻塞 / 唤醒
tg-rcore-tutorial-easy-fs 文件系统(File System)
SuperBlock / Inode / 位图(Bitmap)
DiskInode(直接+间接索引)
块缓存(Block Cache)
BlockDevice trait
文件句柄(FileHandle)
打开标志(OpenFlags)
管道(Pipe)/ 环形缓冲区
用户缓冲区(UserBuffer)
FSManager trait
easy-fs 五层架构实现
文件创建 / 读写 / 目录操作
块缓存管理
管道环形缓冲区实现
FSManager trait 抽象
tg-rcore-tutorial-signal-defs 信号编号(SignalNo)
SIGKILL / SIGINT / SIGUSR1 等
信号动作(SignalAction)
信号集(SignalSet)
最大信号数(MAX_SIG)
信号编号枚举定义
信号动作结构定义
信号集类型定义
为 tg-rcore-tutorial-signal 和 tg-rcore-tutorial-syscall 提供共用类型
tg-rcore-tutorial-signal 信号处理(Signal Handling)
Signal trait 接口
add_signal / handle_signals
get_action_ref / set_action
update_mask / sig_return / from_fork
SignalResult(Handled / ProcessKilled)
Signal trait 接口定义
信号添加 / 处理 / 动作设置
屏蔽字更新 / 信号返回
fork 继承
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-signal-defs
tg-rcore-tutorial-signal-impl SignalImpl 结构
已接收信号位图(received)
信号屏蔽字(mask)
信号处理中状态(handling)
信号动作表(actions)
信号处理函数调用
上下文保存与恢复
Signal trait 的参考实现
信号接收位图管理
屏蔽字逻辑
处理状态和动作表
tg-rcore-tutorial-kernel-context
tg-rcore-tutorial-signal
tg-rcore-tutorial-sync 互斥锁(Mutex trait: lock / unlock)
阻塞互斥锁(MutexBlocking)
信号量(Semaphore: up / down)
条件变量(Condvar: signal / wait_with_mutex)
等待队列(VecDeque<ThreadId>)
UPIntrFreeCell
MutexBlocking 阻塞互斥锁
Semaphore 信号量
Condvar 条件变量
通过 ThreadId 与调度器交互
tg-rcore-tutorial-task-manage
tg-rcore-tutorial-user 用户态程序(User-space App)
用户库(User Library)
系统调用封装(syscall wrapper)
用户堆分配器
用户态 print! / println!
用户测试程序运行时库
系统调用封装
用户堆分配器
各章节测试用例(ch2~ch8)
tg-rcore-tutorial-console
tg-rcore-tutorial-syscall
tg-rcore-tutorial-checker 测试验证
输出模式匹配
正则表达式(Regex)
测试用例判定
rCore-Tutorial CLI 测试输出检查工具
验证内核输出匹配预期模式
支持 --ch N 和 --exercise 模式
tg-rcore-tutorial-linker 链接脚本(Linker Script)
内核内存布局(KernelLayout)
.text / .rodata / .data / .bss / .boot 段
入口点(boot0! 宏)
BSS 段清零
形成内核空间布局的链接脚本模板
用于 build.rs 工具构建 linker.ld
内核布局定位(KernelLayout::locate)
入口宏(boot0!)
段信息迭代

License

Licensed under GNU GENERAL PUBLIC LICENSE, Version 3.0.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors