# 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-ch3 目录下完成实验。
- 目录结构说明:
```
tg-ch3/
├── Cargo.toml(内核配置文件)
├── src/(内核源代码,需要修改)
│ ├── main.rs(内核主函数,包括系统调用接口实现)
│ └── task.rs(任务控制块)
└── tg-user/(用户程序,运行时自动拉取,无需修改)
└── src/bin(测试用例)
```
> **说明**:
> - `tg-user` 会在运行时自动拉取到 `tg-ch3/tg-user` 目录下
> - 只需修改 `tg-ch3/src/` 目录下的内核代码
- 运行练习测例:
```bash
cargo run --features exercise
```
- 测试练习测例:
```bash
./test.sh exercise
```