在内核中实现系统调用

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

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

改进中断服务例程

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

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

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

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

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

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

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

添加 syscall 处理

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

  1. // src/syscall.rs
  2. use crate::context::TrapFrame;
  3. use crate::process;
  4. pub const SYS_WRITE: usize = 64;
  5. pub const SYS_EXIT: usize = 93;
  6. pub fn syscall(id: usize, args: [usize; 3], tf: &mut TrapFrame) -> isize {
  7. match id {
  8. SYS_WRITE => {
  9. print!("{}", args[0] as u8 as char);
  10. 0
  11. },
  12. SYS_EXIT => {
  13. sys_exit(args[0]);
  14. 0
  15. },
  16. _ => {
  17. panic!("unknown syscall id {}", id);
  18. },
  19. }
  20. }
  21. fn sys_exit(code: usize) {
  22. process::exit(code);
  23. }

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