|
| 1 | +# ch3 `sys_trace` 实验报告 |
| 2 | + |
| 3 | +## 1. 实验目标 |
| 4 | +在 ch3 已支持多任务分时调度的基础上,实现新的系统调用: |
| 5 | + |
| 6 | +- 接口:`fn sys_trace(trace_request: usize, id: usize, data: usize) -> isize` |
| 7 | +- 系统调用号:`410` |
| 8 | + |
| 9 | +并满足三种行为: |
| 10 | + |
| 11 | +1. `trace_request = 0`:读取当前任务地址 `id` 处 1 字节数据并返回。 |
| 12 | +2. `trace_request = 1`:向当前任务地址 `id` 写入 `data as u8`,返回 `0`。 |
| 13 | +3. `trace_request = 2`:查询当前任务系统调用编号为 `id` 的调用次数,且本次 `sys_trace` 也要计入统计。 |
| 14 | +4. 其他 `trace_request` 返回 `-1`。 |
| 15 | + |
| 16 | +## 2. 需求分析与设计思路 |
| 17 | + |
| 18 | +### 2.1 关键需求 |
| 19 | +- 需要按“任务”维度统计系统调用次数,而不是全局统计。 |
| 20 | +- `trace_request = 2` 时,本次 `sys_trace` 调用本身也要被统计。 |
| 21 | +- 本章不要求地址安全检查,允许直接读写用户地址。 |
| 22 | + |
| 23 | +### 2.2 设计决策 |
| 24 | +为保证统计逻辑统一、且不遗漏任何系统调用,采用如下方案: |
| 25 | + |
| 26 | +- 在内核系统调用总入口 `syscall()` 中统一计数。 |
| 27 | +- 每个任务维护一个独立的 syscall 计数数组。 |
| 28 | +- `sys_trace(request=2)` 仅做查询,不在 `sys_trace` 内重复计数。 |
| 29 | + |
| 30 | +这样可确保: |
| 31 | +- 任何 syscall 都会被自动统计; |
| 32 | +- 查询 `SYSCALL_TRACE(410)` 时,本次调用一定已经被计入。 |
| 33 | + |
| 34 | +## 3. 具体实现 |
| 35 | + |
| 36 | +### 3.1 数据结构扩展(按任务统计) |
| 37 | +文件:`os/src/task/task.rs` |
| 38 | + |
| 39 | +- 新增常量:`MAX_SYSCALL_NUM: usize = 512` |
| 40 | +- 在 `TaskControlBlock` 中新增字段: |
| 41 | + - `syscall_times: [usize; MAX_SYSCALL_NUM]` |
| 42 | + |
| 43 | +作用:为每个任务保存独立的 syscall 计数表,索引即 syscall id。 |
| 44 | + |
| 45 | +### 3.2 任务初始化与访问接口 |
| 46 | +文件:`os/src/task/mod.rs` |
| 47 | + |
| 48 | +- 在任务数组初始化时,将 `syscall_times` 全部置零。 |
| 49 | +- 新增接口: |
| 50 | + - `record_current_syscall(syscall_id: usize)`:给当前运行任务对应 syscall 计数加一。 |
| 51 | + - `current_task_syscall_count(syscall_id: usize) -> usize`:读取当前任务某个 syscall 的计数。 |
| 52 | +- 边界处理:当 `syscall_id >= MAX_SYSCALL_NUM` 时,写入忽略、查询返回 `0`,避免越界。 |
| 53 | + |
| 54 | +### 3.3 在 syscall 总入口统一计数 |
| 55 | +文件:`os/src/syscall/mod.rs` |
| 56 | + |
| 57 | +在 `pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize` 的 `match` 分发前增加: |
| 58 | + |
| 59 | +```rust |
| 60 | +record_current_syscall(syscall_id); |
| 61 | +``` |
| 62 | + |
| 63 | +这一步是本次实现的核心: |
| 64 | +- 统一入口计数,避免分散在各个 `sys_xxx` 中导致遗漏; |
| 65 | +- 满足“本次 `sys_trace` 也计入统计”的要求。 |
| 66 | + |
| 67 | +### 3.4 实现 `sys_trace` 三种语义 |
| 68 | +文件:`os/src/syscall/process.rs` |
| 69 | + |
| 70 | +实现如下逻辑: |
| 71 | + |
| 72 | +- `trace_request == 0`:`id` 按 `*const u8` 解引用并返回。 |
| 73 | +- `trace_request == 1`:`id` 按 `*mut u8` 写入 `data as u8`,返回 `0`。 |
| 74 | +- `trace_request == 2`:返回 `current_task_syscall_count(id) as isize`。 |
| 75 | +- 其他:返回 `-1`。 |
| 76 | + |
| 77 | +## 4. 关键正确性说明 |
| 78 | + |
| 79 | +### 4.1 为什么本次 `sys_trace` 会被计入? |
| 80 | +调用路径是: |
| 81 | + |
| 82 | +用户态 `ecall` -> 内核 `syscall()` 总入口 -> `record_current_syscall(410)` -> 分发到 `sys_trace()`。 |
| 83 | + |
| 84 | +因此在 `sys_trace(request=2)` 查询时,当前这次 `sys_trace` 已经先被统计,满足题目要求。 |
| 85 | + |
| 86 | +### 4.2 为什么使用“每任务计数”而不是全局计数? |
| 87 | +题目要求追踪“当前任务”的系统调用历史。使用 TCB 内部计数数组可天然保证任务间互不干扰。 |
| 88 | + |
| 89 | +## 5. 测试与验证 |
| 90 | +执行命令: |
| 91 | + |
| 92 | +```bash |
| 93 | +cd os |
| 94 | +make run CHAPTER=3 TEST=3 BASE=0 |
| 95 | +``` |
| 96 | + |
| 97 | +该命令会构建并运行 ch3 普通测试(包含 `ch3_trace`)。 |
| 98 | + |
| 99 | +关键输出包含: |
| 100 | +- `Test trace OK!` |
| 101 | +- `Test sleep1 passed!` |
| 102 | +- `Test sleep OK!` |
| 103 | + |
| 104 | +说明:`sys_trace` 的读/写/计数语义均通过当前实验用例验证。 |
| 105 | + |
| 106 | +## 6. 结果总结 |
| 107 | +本次修改完成了 ch3 对 `sys_trace(410)` 的支持,核心点是: |
| 108 | + |
| 109 | +- 在 syscall 总入口统一计数; |
| 110 | +- 将计数数据放入任务控制块实现“按任务追踪”; |
| 111 | +- 按题目定义实现 `sys_trace` 三种请求语义; |
| 112 | +- 通过 `ch3_trace` 用例验证功能正确。 |
| 113 | + |
| 114 | +后续在实现地址空间后,可进一步为读写地址行为增加合法性检查与隔离机制。 |
0 commit comments