## 实验六：系统调用

### 实验之前

- 阅读实验指导五、六。

### 实验题目

1.  原理：使用条件变量之后，分别从线程和操作系统的角度而言读取字符的系统调用是阻塞的还是非阻塞的？

    {% reveal %}
> 对于线程而言，是阻塞的，因为在等待有效输入之前线程都会暂停。但对于操作系统而言，等待输入的时间完全分配给了其他线程，所以对于操作系统来说是非阻塞的。
    {% endreveal %}

    <br>
2.  设计：如果要让用户线程能够使用 `Vec` 等，需要做哪些工作？如果要让用户线程能够使用大于其栈大小的动态分配空间，需要做哪些工作？

3.  实验：实现 `get_tid` 系统调用，使得用户线程可以获取自身的线程 ID。

4.  实验：基于你在实验四（上）的实践，实现 `sys_fork` 系统调用。该系统调用复制一个进程，并为父进程返回 1（目前没有引入进程 ID，也可以自行补充为进程 ID），而为子进程返回 0。

    相比于实验四，你可能需要额外注意文件描述符的复制。

5.  实验：将一个文件打包进用户镜像，并让一个用户进程读取它并打印其内容。需要实现 `sys_open`，将文件描述符加入进程的 `descriptors` 中并返回，然后通过 `sys_read` 来读取。

6.  挑战实验：实现 `sys_pipe`，为进程添加并返回两个文件描述符，分别为一个管道的读和写端。用户进程调用完 `sys_pipe` 后调用 `sys_fork`，父进程写入管道，子进程可以读取。读取时尽量避免忙等待。

    ```rust
    /// 用户进程样例
    pub fn main() -> usize {
        let (mut write_fd, mut read_fd) = sys_pipe();
        if sys_fork() {
            // 父进程
            sys_close(read_fd); // 不一定需要实现
            sys_write(write_fd, "hello_world".as_bytes());
        } else {
            // 子进程
            sys_close(write_fd); // 不一定需要实现
            let mut buffer = [0u8; 64];
            let len = sys_read(read_fd, &mut buffer);
            println!("{}", core::str::from_utf8(&buffer));
        }
    }
    ```
