在内核中实现系统调用

上一节中描述的Hello World应用程序会发出两个系统调用请求,我们的 OS 当然也就需要实现这两个系统调用:

  1. 在屏幕上输出一个字符
  2. 结束运行,退出当前线程

改进中断服务例程

这些功能其实我们的内核都已经实现完毕,因此重点是将系统调用这条调用链建立起来。

1. // src/interrupt.rs

3. #[no_mangle]
4. pub fn rust_trap(tf: &mut TrapFrame) {
5. match tf.scause.cause() {
6. ...
7. Trap::Exception(Exception::UserEnvCall) => syscall(tf),
8. ...
9. }
10. }

首先是发现中断原因是在用户态执行 ecall 指令时,说明用户程序向我们请求服务,我们转入 syscall 函数。

1. // src/interrupt.rs

3. fn syscall(tf: &mut TrapFrame) {
4. // 返回后跳转到 ecall 下一条指令
5. tf.sepc += 4;
6. let ret = crate::syscall::syscall(
7. tf.x[17],
8. [tf.x[10], tf.x[11], tf.x[12]],
9. tf
10. );
11. tf.x[10] = ret as usize;
12. }

我们从中断帧中取出中断之前的寄存器

在内核中实现系统调用 - 图1

的值,分别表示 syscall id 以及传入的参数。这是通过用户态的内联汇编 ecall 传给我们的。

添加 syscall 处理

我们将系统调用单开一个模块来实现:

1. // src/syscall.rs

3. use crate::context::TrapFrame;
4. use crate::process;

6. pub const SYS_WRITE: usize = 64;
7. pub const SYS_EXIT: usize = 93;

9. pub fn syscall(id: usize, args: [usize; 3], tf: &mut TrapFrame) -> isize {
10. match id {
11. SYS_WRITE => {
12. print!("{}", args[0] as u8 as char);
13. 0
14. },
15. SYS_EXIT => {
16. sys_exit(args[0]);
17. 0
18. },
19. _ => {
20. panic!("unknown syscall id {}", id);
21. },
22. }
23. }

25. fn sys_exit(code: usize) {
26. process::exit(code);
27. }

不必花太多功夫,我们就在内核中支持了两个系统调用!