系统调用

在32位操作系统中,常常使用int 0x80指令来完成系统调用处理。但是在x64中,引入了syscall指令,称为快速系统调用,其不触发中断,而是有自己的一套控制流,在用户态使用syscall指令后,CPU会执行以下动作:

  • 从CPU特殊寄存器STAR MSR中加载cs、ss寄存器
  • 将当前的rflags存入r11寄存器,从CPU特殊寄存器RFMASK MSR中加载rflags
  • 将当前rip存入rcx,从CPU特殊寄存器LSTAR MSR中加载rip

内核系统调用分发

/// 系统调用总控函数
pub fn syscall(syscall_id: usize, args: [usize; 6]) -> (usize, usize) {
    let syscall_id = num::FromPrimitive::from_usize(syscall_id).unwrap();
    let ret = match syscall_id {
        // 调试用
        DebugWrite => sys_debug_write(args[0]),
        ...

        // 任务相关
        ProcExit => sys_proc_exit(args[0]),
        ...

        // 文件相关
        Open => sys_open(args[0], args[1], args[2]),
        ...

        // 同步互斥
        MutexCreate => sys_mutex_create(),
        ...
    };
    ret
}

用户态syscall

/// 用户态使用系统调用
fn syscall(id: SyscallNum, args: [usize; 6]) -> (usize, usize) {
    let mut ret0: usize;
    let mut ret1: usize;
    unsafe {
        core::arch::asm!(
            "syscall",
            in("rax") id as usize,
            in("rdi") args[0],
            in("rsi") args[1],
            in("rdx") args[2],
            in("r10") args[3],
            in("r8") args[4],
            in("r9") args[5],
            out("rcx") _,
            out("r11") _,
            lateout("rax") ret0,
            lateout("rdx") ret1,
        );
    }
    (ret0, ret1)
}

用户态使用内联汇编来实现系统调用,将rax、rdi、rsi、rdx等寄存器作为参数,rax存放系统调用号,最终返回值存放在rax和rdx中。用户态使用不同系统调用的方法如下:

fn sys_proc_exit(exit_code: usize) -> (usize, usize) {
    syscall(SyscallNum::ProcExit, [exit_code, 0, 0, 0, 0, 0])
}

fn sys_proc_wait(pid: usize) -> (usize, usize) {
    syscall(SyscallNum::ProcWait, [pid, 0, 0, 0, 0, 0])
}
...