系统调用

15 阅读1分钟

xv6 系统调用

sleep.c通过编译、链接为可执行文件

shell中输入sleep n,通过命令行传参机制,forkexec替换为sleep,并获取参数n

执行sleep.c的逻辑,关键为sleep(time)语句,该sleep()user.h声明。

usys.pl脚本统一生成了sleep的“实现”,是汇编码的形式,如下。

.global sleep
sleep:
 li a7, SYS_sleep
 ecall
 ret

SYS_sleep是个宏,为整形13,是系统调用号。把13加载到a7寄存器后执行RISC-V 硬件指令ecall。其中包含(省略保存寄存器等其他操作)

void
syscall(void)  //位于syscall.c
{
  int num;
  struct proc *p = myproc();
  num = p->trapframe->a7;      
  if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
    p->trapframe->a0 = syscalls[num]();
  } else {
    printf("%d %s: unknown sys call %d\n", p->pid, p->name, num);
    p->trapframe->a0 = -1;
  }
}

根据索引num,在函数指针数组找到对应的函数指针填入a0寄存器

//函数指针数组
static uint64 (*syscalls[])(void) = {//(void)在这里的意思是:这个数组里存放的所有函数,都不接收任何参数
[SYS_fork]    sys_fork,
...
[SYS_sleep]   sys_sleep,
...
};
sys_sleep()    //sysproc.c 感觉还是封装

   👇️调用
    
sleep()        //proc.c 真正干活的

区别

特性xv6 (教学 OS)Linux (工业级 OS)
核心流程用户 -> 陷入 -> 查表 -> 执行完全一致
触发方式ecall (RISC-V)syscall (x86), ecall (RISC-V)
C函数参数void (内部手动抓取)带参数 (宏自动映射寄存器)
分发方式简单的 syscalls[] 数组复杂的 sys_call_table + 审计/追踪钩子
数量级~21 个~450+ 个
上下文切换简单,全保存极度优化,根据情况保存