# 第七章：进程间通信与信号

本章在第六章"文件系统"的基础上，引入了两大新机制：

1. **管道（Pipe）**：基于文件描述符的进程间通信机制，用于父子进程之间的单向数据传递
2. **信号（Signal）**：异步事件通知机制，允许一个进程向另一个进程发送事件通知

通过本章的学习和实践，你将理解：

- 管道的概念、创建和使用流程
- 管道的内部实现：环形缓冲区（Ring Buffer）
- 统一的文件描述符抽象：文件、管道、标准 I/O 共享同一套接口
- 信号的概念：信号集、信号屏蔽字、信号处理函数
- 信号的发送（kill）、注册（sigaction）、屏蔽（sigprocmask）和返回（sigreturn）
- 命令行参数的传递和 I/O 重定向的原理

> **前置知识**：建议先完成第一章至第六章的学习，理解裸机启动、Trap 处理、系统调用、多任务调度、虚拟内存、进程管理和文件系统。

## 练习任务（以教代学，学以致用）：

- 学：读本文件，了解相关OS知识，在某个开发环境（在线或本地）中正确编译运行rcore-tutorial-ch7。
- 教：分析并改进rcore-tutorial-ch7的文档和代码，让自己更高效地完成本章学习。
- 用：基于rcore-tutorial-ch7的源代码，实现用户态简化版吃豆人游戏应用，还原经典玩法核心等基本功能；并扩展操作系统内核功能，支持用户态简化版吃豆人游戏应用。[demo](https://github.com/rcore-os/tg-rcore-tutorial-game-demo/blob/main/ch7-pacman.gif)

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

## 项目结构

```
ch7/
├── .cargo/
│   └── config.toml     # Cargo 配置：交叉编译目标和 QEMU runner
├── .gitignore           # Git 忽略规则
├── build.rs            # 构建脚本：编译用户程序，打包 easy-fs 磁盘镜像
├── Cargo.toml          # 项目配置与依赖
├── LICENSE             # GPL v3 许可证
├── README.md           # 本文档
├── rust-toolchain.toml # Rust 工具链配置
├── test.sh             # 自动测试脚本
└── src/
    ├── main.rs         # 内核主体：初始化、调度循环、系统调用实现（含信号处理）
    ├── fs.rs           # 文件系统管理 + 统一的 Fd 枚举
    ├── process.rs      # 进程结构：含 fd_table 和 signal
    ├── processor.rs    # 处理器管理：进程管理器
    └── virtio_block.rs # VirtIO 块设备驱动
```

<a id="source-nav"></a>

## 源码阅读导航索引

[返回根文档导航总表](../README.md#chapters-source-nav-map)

本章建议按“统一 fd 抽象 -> 管道数据流 -> 信号状态机”三条线并行阅读。

| 阅读顺序 | 文件 | 重点问题 |
|---|---|---|
| 1 | `src/fs.rs` | 为什么要用 `Fd` 枚举统一文件/管道/标准 I/O？ |
| 2 | `src/main.rs` 的 `impls::IO` | `pipe`、`read`、`write` 如何共享同一套 fd_table 入口？ |
| 3 | `src/process.rs` | 进程信号状态在 `from_elf`、`fork`、`exec` 中如何继承/重置？ |
| 4 | `src/main.rs` Trap 主循环 | 为什么在 syscall 返回前处理信号，处理结果如何影响进程存活？ |
| 5 | `src/main.rs` 的 `impls::Signal` | `kill/sigaction/sigprocmask/sigreturn` 的内核语义是什么？ |

配套建议：结合 `tg-rcore-tutorial-signal` / `tg-rcore-tutorial-signal-impl` 注释阅读，重点关注 `handle_signals` 的决策流程。

## DoD 验收标准（本章完成判据）

- [ ] 能解释为什么要用统一 `Fd` 枚举承载“文件/管道/标准IO”
- [ ] 能从代码说明 `pipe` 建立后父子进程如何通过读写端通信
- [ ] 能说明信号的发送、注册、屏蔽、返回四个关键 syscall 语义
- [ ] 能解释“syscall 返回前检查信号”对进程退出路径的影响
- [ ] 能执行 `./test.sh base` 并通过本章基础测试

## 概念-源码-测试三联表

| 核心概念 | 源码入口 | 自测方式（命令/现象） |
|---|---|---|
| 统一 fd 抽象 | `tg-rcore-tutorial-ch7/src/fs.rs` 的 `Fd` 枚举 | 同一 `read/write` 入口可操作文件与管道 |
| 管道通信 | `tg-rcore-tutorial-ch7/src/main.rs` 的 `impls::IO::pipe` | 运行 `tg-rcore-tutorial-ch7b_pipetest` 可观察父子通信成功 |
| 信号状态管理 | `tg-rcore-tutorial-ch7/src/process.rs` 的 `signal` 字段与 `fork` 继承 | 子进程信号处理配置符合预期 |
| 信号 syscall | `tg-rcore-tutorial-ch7/src/main.rs` 的 `impls::Signal` | `kill/sigaction/sigprocmask/sigreturn` 行为正确 |

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

## 一、环境准备

### 1.1 安装 Rust 工具链

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

验证：

```bash
rustc --version    # 要求 >= 1.85.0（支持 edition 2024）
cargo --version
```

### 1.2 添加 RISC-V 64 编译目标

```bash
rustup target add riscv64gc-unknown-none-elf
```

### 1.3 安装 QEMU 模拟器

**Ubuntu / Debian：**

```bash
sudo apt update
sudo apt install qemu-system-misc
```

**macOS（Homebrew）：**

```bash
brew install qemu
```

验证：

```bash
qemu-system-riscv64 --version    # 建议 >= 7.0
```

### 1.4 安装额外工具

```bash
cargo install cargo-clone
cargo install cargo-binutils
rustup component add llvm-tools
```

### 1.5 获取源代码

**方式一：只获取本实验**

```bash
cargo clone tg-rcore-tutorial-ch7
cd tg-rcore-tutorial-ch7
```

**方式二：获取所有实验**

```bash
git clone --recurse-submodules https://github.com/rcore-os/tg-rcore-tutorial.git
cd tg-rcore-tutorial-ch7
```

## 二、编译与运行

### 2.1 编译

```bash
cargo build
```

构建过程与第六章相同：`build.rs` 会自动下载编译 `tg-rcore-tutorial-user` 用户程序，打包到 `fs.img` 磁盘镜像中。

> 环境变量说明：
> - `TG_USER_DIR`：指定本地 tg-rcore-tutorial-user 源码路径
> - `TG_USER_VERSION`：指定 tg-rcore-tutorial-user 版本（默认 `0.2.0-preview.1`）
> - `TG_SKIP_USER_APPS`：跳过用户程序编译
> - `LOG`：设置日志级别

### 2.2 运行

```bash
cargo run
```

QEMU 命令与第六章相同（挂载 fs.img 块设备）：

```bash
qemu-system-riscv64 \
    -machine virt \
    -nographic \
    -bios none \
    -drive file=target/riscv64gc-unknown-none-elf/debug/fs.img,if=none,format=raw,id=x0 \
    -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 \
    -kernel target/riscv64gc-unknown-none-elf/debug/tg-rcore-tutorial-ch7
```

### 2.3 预期输出

```
[tg-rcore-tutorial-ch7 ...] Hello, world!
[ INFO] .text    ---> 0x80200000..0x8023xxxx
[ INFO] .rodata  ---> 0x8023xxxx..0x8024xxxx
[ INFO] .data    ---> 0x8024xxxx..0x81exxxxx
[ INFO] .boot    ---> 0x81exxxxx..0x81exxxxx
[ INFO] (heap)   ---> 0x81exxxxx..0x83200000
[ INFO] MMIO range -> 0x10001000..0x10002000

Rust user shell
>> ch7b_pipetest
Read OK, child process exited!
pipetest passed!
Shell: Process 2 exited with code 0
>>
```

你可以在 Shell 中运行管道测试程序：
- `tg-rcore-tutorial-ch7b_pipetest`：父进程通过管道向子进程传递字符串
- `tg-rcore-tutorial-ch7b_pipe_large_test`：通过两个管道实现父子进程双向通信

### 2.4 运行测试

```bash
./test.sh           # 运行基础测试
./test.sh base      # 等价于上面
./test.sh all       # 运行全部测试（ch7 仅有基础测试）
```

---

## 三、操作系统核心概念

### 3.1 为什么需要进程间通信？

在前几章中，各个进程是完全独立的，它们无法直接交换数据。但在实际操作系统中，进程协作是非常常见的需求：

| 场景 | 说明 |
|------|------|
| 管道命令 | `cat file.txt \| grep "hello"` —— 前一个进程的输出作为后一个进程的输入 |
| 服务通信 | Web 服务器将请求转发给后端处理进程 |
| 父子协作 | 父进程 fork 子进程后通过管道交换数据 |

**管道** 是最基本的 IPC 机制之一，它提供了一种简单的单向数据通道。

### 3.2 管道（Pipe）

#### 管道的基本概念

管道是一对文件描述符：**读端**（read end）和**写端**（write end），数据从写端流向读端。

```text
┌──────────┐     ┌──────────────────────────┐     ┌──────────┐
│ 父进程    │     │         管道              │     │ 子进程    │
│          │     │  ┌──────────────────────┐ │     │          │
│ write(fd) ──→  │  │   环形缓冲区          │ │  ──→ read(fd) │
│          │     │  │  [H|e|l|l|o|_|_|_|_] │ │     │          │
│          │     │  └──────────────────────┘ │     │          │
└──────────┘     └──────────────────────────┘     └──────────┘
    写端                                               读端
```

#### 管道的创建和使用

典型的管道使用流程：

```text
1. 父进程调用 pipe() 创建管道
   ┌─────────────────────┐
   │ fd_table:            │
   │   ...                │
   │   [3] = PipeRead     │  ← 读端
   │   [4] = PipeWrite    │  ← 写端
   └─────────────────────┘

2. 父进程调用 fork() 创建子进程
   父进程和子进程都拥有 fd[3]（读端）和 fd[4]（写端）

3. 子进程关闭写端 close(4)，父进程关闭读端 close(3)
   父进程：write(4, "Hello")  →  管道  →  子进程：read(3, buf)

4. 通信完毕后，各自关闭剩余的 fd
```

#### 管道的内部实现：环形缓冲区

管道内部使用**环形缓冲区**（Ring Buffer）存储数据：

```text
PipeRingBuffer:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ A │ B │ C │   │   │   │   │   │
└───┴───┴───┴───┴───┴───┴───┴───┘
  ↑head          ↑tail
  读取位置       写入位置

状态：
- EMPTY：head == tail 且已知为空
- FULL：head == tail 且已知为满
- NORMAL：其他情况
```

关键行为：
- **读取（read）**：从 head 位置读取，head 向前移动。如果缓冲区为空，暂停当前进程等待数据。
- **写入（write）**：向 tail 位置写入，tail 向前移动。如果缓冲区满，暂停当前进程等待读取。
- **所有写端关闭**：缓冲区中残余数据读完后，read 返回 0 表示 EOF。

#### pipe 系统调用

```rust
/// 创建管道
/// pipe: 用户空间的 usize[2] 数组地址
/// 返回：pipe[0] = 读端 fd，pipe[1] = 写端 fd
fn pipe(&self, pipe: usize) -> isize
```

### 3.3 统一的文件描述符类型（Fd 枚举）

本章引入了 `Fd` 枚举，将所有文件描述符类型统一管理：

```rust
pub enum Fd {
    File(FileHandle),           // 普通磁盘文件
    PipeRead(PipeReader),       // 管道读端
    PipeWrite(Arc<PipeWriter>), // 管道写端
    Empty { read, write },      // 空描述符（stdin/stdout/stderr）
}
```

统一接口的好处：
- read/write 系统调用不需要知道 fd 的具体类型
- 文件和管道可以混合使用（如 I/O 重定向）
- fork 时子进程自动继承所有类型的 fd

### 3.4 信号（Signal）

#### 信号的基本概念

信号是**异步事件通知**机制。进程可以在任意时刻收到信号，类似于硬件中断对 CPU 的打断。

常见信号：

| 信号 | 编号 | 默认行为 | 说明 |
|------|------|----------|------|
| SIGKILL | 9 | 终止进程 | 不可捕获、不可忽略 |
| SIGINT | 2 | 终止进程 | Ctrl+C |
| SIGTERM | 15 | 终止进程 | 请求终止 |
| SIGCHLD | 17 | 忽略 | 子进程状态变化 |
| SIGUSR1/2 | 10/12 | 终止进程 | 用户自定义 |

#### 信号处理流程

```text
进程 A                          进程 B
  │                               │
  │── kill(pid_B, SIGUSR1) ──→    │
  │                               │ （信号加入 received 位图）
  │                               │
  │                               │ ← syscall 返回前检查信号
  │                               │
  │                               │ 调用注册的信号处理函数
  │                               │ （或执行默认行为）
  │                               │
  │                               │ ← sigreturn 恢复原执行流
```

#### 信号相关数据结构

每个进程的信号处理器（`SignalImpl`）维护：

```rust
pub struct SignalImpl {
    received: SignalSet,                        // 已接收的信号（位图）
    mask: SignalSet,                            // 信号屏蔽字（被屏蔽的信号不处理）
    handling: Option<HandlingSignal>,           // 正在处理的信号状态
    actions: [Option<SignalAction>; MAX_SIG+1], // 信号处理函数表
}
```

#### 信号相关系统调用

| 系统调用 | 功能 |
|----------|------|
| `kill(pid, signum)` | 向指定进程发送信号 |
| `sigaction(signum, action, old_action)` | 设置/获取信号处理函数 |
| `sigprocmask(mask)` | 更新信号屏蔽字 |
| `sigreturn()` | 从信号处理函数返回，恢复原上下文 |

#### 信号处理时机

在本实现中，信号处理发生在**系统调用返回用户态之前**：

```rust
// 系统调用执行完毕后
match task.signal.handle_signals(ctx) {
    SignalResult::ProcessKilled(exit_code) => {
        // 收到终止信号，进程退出
    },
    _ => {
        // 正常返回系统调用结果
    }
}
```

### 3.5 命令行参数与 I/O 重定向

虽然本 tg-rcore-tutorial-ch7 实现未完整支持命令行参数传递，但这是第七章教学内容的重要部分。

#### 命令行参数

```text
exec("program", ["arg1", "arg2"])

用户栈布局：
┌──────────────┐ 高地址
│ argv[0] 指针  │ → "program\0"
│ argv[1] 指针  │ → "arg1\0"
│ argv[2] 指针  │ → "arg2\0"
│ 0 (终止符)    │
├──────────────┤
│ "program\0"  │
│ "arg1\0"     │
│ "arg2\0"     │
├──────────────┤ ← user_sp（对齐到 8 字节）
│   ...        │
└──────────────┘ 低地址
```

进入用户态时，`a0 = argc`（参数个数），`a1 = argv`（参数数组指针）。

#### I/O 重定向

I/O 重定向通过文件描述符的"关闭-复制"技巧实现：

```text
输出重定向：program > output.txt

1. fork() 创建子进程
2. 子进程中：
   fd = open("output.txt", CREATE|WRONLY)  // 假设 fd = 3
   close(1)                                 // 关闭 stdout
   dup(3)                                   // 复制 fd 3 → 得到 fd 1
   close(3)                                 // 关闭原始 fd
   exec("program")                          // 程序的 stdout 现在指向 output.txt
```

### 3.6 系统调用汇总

| syscall ID | 名称 | 功能 | 状态 |
|-----------|------|------|------|
| 59 | `pipe` | 创建管道 | **新增** |
| 129 | `kill` | 发送信号 | **新增** |
| 134 | `sigaction` | 设置信号处理 | **新增** |
| 135 | `sigprocmask` | 设置屏蔽字 | **新增** |
| 139 | `sigreturn` | 信号返回 | **新增** |
| 56 | `open` | 打开文件 | 继承 |
| 57 | `close` | 关闭 fd | 继承 |
| 63 | `read` | 读取（**扩展**：支持管道） | 扩展 |
| 64 | `write` | 写入（**扩展**：支持管道） | 扩展 |
| 93 | `exit` | 退出进程 | 继承 |
| 220 | `fork` | 创建子进程（**扩展**：继承信号配置） | 扩展 |
| 221 | `exec` | 替换程序 | 继承 |
| 260 | `wait` | 等待子进程 | 继承 |

---

## 四、代码解读

### 4.1 `src/main.rs` —— 内核主体

**与第六章的区别：**
- 新增 `tg_syscall::init_signal()` 初始化信号系统调用
- 主调度循环中，系统调用返回前新增**信号处理**：
  - `SignalResult::ProcessKilled`：进程被终止信号杀死
  - 其他情况：正常处理系统调用返回值
- `impls` 模块新增 `Signal` trait 实现（kill/sigaction/sigprocmask/sigreturn）
- `impls` 模块新增 `pipe` 系统调用实现

### 4.2 `src/fs.rs` —— 文件系统管理 + Fd 枚举

**核心变化：统一的 `Fd` 枚举**
- `Fd::File(FileHandle)`：普通磁盘文件
- `Fd::PipeRead(PipeReader)`：管道读端
- `Fd::PipeWrite(Arc<PipeWriter>)`：管道写端
- `Fd::Empty { read, write }`：空描述符（stdin/stdout/stderr）

所有类型都实现了 `readable()`、`writable()`、`read()`、`write()` 方法。

### 4.3 `src/process.rs` —— 进程管理

**与第六章的区别：**
- `fd_table` 类型从 `Vec<Option<Mutex<FileHandle>>>` 变为 `Vec<Option<Mutex<Fd>>>`
- 新增 `signal: Box<dyn Signal>` 字段
- `fork()` 通过 `self.signal.from_fork()` 让子进程继承信号配置
- `from_elf()` 使用 `Fd::Empty` 初始化 stdin/stdout/stderr

### 4.4 `Cargo.toml` —— 依赖说明

与第六章相比新增的依赖：

| 依赖 | 说明 |
|------|------|
| `tg-rcore-tutorial-signal` | 信号模块 trait 定义（Signal、SignalResult、SignalNo） |
| `tg-rcore-tutorial-signal-impl` | 信号模块参考实现（SignalImpl） |

**信号 crate 依赖关系：**

```text
tg-rcore-tutorial-ch7
  ├── tg-rcore-tutorial-signal-impl → tg-rcore-tutorial-signal → tg-rcore-tutorial-signal-defs → numeric-enum-macro
  ├── tg-rcore-tutorial-signal
  └── tg-rcore-tutorial-syscall → tg-rcore-tutorial-signal-defs
```

---

## 五、本章小结

通过本章的学习和实践，你完成了操作系统中进程间通信的核心机制：

1. **管道机制**：基于环形缓冲区的单向数据通道，通过 pipe 系统调用创建
2. **统一文件描述符**：Fd 枚举将文件、管道、标准 I/O 统一为一套接口
3. **信号机制**：异步事件通知，支持信号发送、处理函数注册、屏蔽和返回
4. **命令行参数**：通过用户栈传递参数给程序
5. **I/O 重定向**：通过文件描述符的关闭-复制技巧实现

## 六、思考题

1. **管道 vs 共享内存**：管道通过内核缓冲区传递数据，而共享内存允许进程直接访问同一块物理内存。各有什么优缺点？什么场景下应该使用哪种？

2. **管道的阻塞语义**：当管道缓冲区为空时，读取操作应该阻塞等待。在本实现中，这种"阻塞"是如何实现的？（提示：任务切换）

3. **信号 vs 管道**：信号和管道都是 IPC 机制，但它们的适用场景有何不同？信号能传递数据吗？

4. **信号处理时机**：本实现中信号在系统调用返回前处理。如果进程正在执行用户态代码（没有系统调用），信号什么时候被处理？理想的实现应该是什么样的？

5. **fork 后的管道共享**：fork 后父子进程共享管道的读端和写端。为什么通常需要在子进程中关闭不需要的一端？如果不关闭会有什么问题？

6. **环形缓冲区的大小**：管道的环形缓冲区大小是固定的。如果写入的数据量超过缓冲区大小会发生什么？Linux 中管道的默认缓冲区大小是多少？

## 参考资料

- [rCore-Tutorial-Guide 第七章](https://learningos.github.io/rCore-Tutorial-Guide/)
- [rCore-Tutorial-Book 第七章](https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter7/index.html)
- [Linux pipe(2) man page](https://man7.org/linux/man-pages/man2/pipe.2.html)
- [Linux signal(7) man page](https://man7.org/linux/man-pages/man7/signal.7.html)
- [UNIX 进程间通信](https://en.wikipedia.org/wiki/Inter-process_communication)

---

## 附录：rCore-Tutorial 组件分析表

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

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

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

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

Licensed under GNU GENERAL PUBLIC LICENSE, Version 3.0.
