# chapter3 练习

## 编程作业

### 获取任务信息

在 ch3 中，我们的系统已经能够支持多个任务分时轮流运行，我们希望引入一个新的系统调用 `sys_trace`（ID 为 410）用来追踪当前任务系统调用的历史信息，并做对应的修改。定义如下：

```rust
fn trace(&self, _caller: tg_syscall::Caller, _trace_request: usize, _id: usize, _data: usize) -> isize
```

- 调用规范：
  - 这个系统调用有三种功能，根据 `trace_request` 的值不同，执行不同的操作：
    - 如果 `trace_request` 为 0，则 `id` 应被视作 `*const u8`，表示读取当前任务 `id` 地址处一个字节的无符号整数值。此时应忽略 `data` 参数。返回值为 `id` 地址处的值。
    - 如果 `trace_request` 为 1，则 `id` 应被视作 `*mut u8`，表示写入 `data`（作为 `u8`，即只考虑最低位的一个字节）到该用户程序 `id` 地址处。返回值应为 0。
    - 如果 `trace_request` 为 2，表示查询当前任务调用编号为 `id` 的系统调用的次数，返回值为这个调用次数。**本次调用也计入统计**。
    - 否则，忽略其他参数，返回值为 -1。

- 说明：
  - 你可能会注意到，这个调用的读写并不安全，使用不当可能导致崩溃。这是因为在下一章节实现地址空间之前，系统中缺乏隔离机制。所以我们 **不要求你实现安全检查机制，只需通过测试用例即可**。
  - 你还可能注意到，这个系统调用读写本任务内存的功能并不是很有用。这是因为作业的灵感来源 syscall 主要依靠 trace 功能追踪其他任务的信息，但在本章节我们还没有进程、线程等概念，所以简化了操作，只要求追踪自身的信息。

### HINT

- 大胆修改已有框架！除了配置文件，你几乎可以随意修改已有框架的内容。
- 系统调用次数可以考虑在 `TaskControlBlock::handle_syscall()` 中统计。
- 可以扩展 `TaskControlBlock` 结构来维护系统调用计数信息。
- 不要害怕使用 `unsafe` 做类型转换，这在内核处理用户调用时是不可避免的。
- 在实现时，可以把系统调用参数中前缀的下划线去掉，这样更清晰。实验框架之所以这么写，是因为在没有使用对应参数的情况下，Rust 推荐使用下划线前缀以避免警告。

### 实验要求

- 在 tg-rcore-tutorial-ch3 目录下完成实验。
- 目录结构说明：

```
tg-rcore-tutorial-ch3/
├── Cargo.toml（内核配置文件）
├── src/（内核源代码，需要修改）
│   ├── main.rs（内核主函数，包括系统调用接口实现）
│   └── task.rs（任务控制块）
└── tg-rcore-tutorial-user/（用户程序，运行时自动拉取，无需修改）
    └── src/bin（测试用例）
```

> **说明**：
> - `tg-rcore-tutorial-user` 会在运行时自动拉取到 `tg-rcore-tutorial-ch3/tg-rcore-tutorial-user` 目录下
> - 只需修改 `tg-rcore-tutorial-ch3/src/` 目录下的内核代码

- 运行练习测例：
```bash
cargo run --features exercise
```

- 测试练习测例：
```bash
./test.sh exercise
```
