tg-rcore-tutorial-ch7 0.4.8

Chapter 7 of rCore Tutorial: Inter-process communication with pipe and signal handling.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# 第七章:进程间通信与信号

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

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.