xv6 系统调用
sleep.c通过编译、链接为可执行文件
在shell中输入sleep n,通过命令行传参机制,fork后exec替换为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+ 个 |
| 上下文切换 | 简单,全保存 | 极度优化,根据情况保存 |