从手册页OS161
:
Synopsis
#include <unistd.h>
#include <fcntl.h>
int
open(const char *filename, int flags);
int
open(const char *filename, int flags, mode_t mode);
标准c库函数是如何实现的open
被定义为:
int open(const char *filename, int flags, ...);
声明:
/*
* Definition for each syscall.
* All we do is load the syscall number into v0, the register the
* kernel expects to find it in, and jump to the shared syscall code.
* (Note that the addiu instruction is in the jump's delay slot.)
*/
#define SYS_open 45
#define SYSCALL(sym, num) \
.set noreorder ; \
.globl sym ; \
.type sym,@function ; \
.ent sym ; \
sym: ; \
j __syscall ; \
addiu v0, $0, SYS_##sym ; \
.end sym ; \
.set reorder
SYSCALL(open, 45)
当发出系统调用时,系统调用调度程序被调用。系统调用调度程序接受一个指向trapframe
其中包括发出系统调用之前寄存器的值。其中一个寄存器包含系统调用号,调度程序使用该号来调度到正确的系统调用函数。调度程序看起来像这样:
void
syscall(struct trapframe *tf)
{
int callno;
...
callno = tf->tf_v0;
...
switch (callno) {
case SYS_reboot:
err = sys_reboot(tf->tf_a0);
break;
case SYS___time:
err = sys___time((userptr_t)tf->tf_a0,
(userptr_t)tf->tf_a1);
...
}
以下注释描述了如何传递参数以及如何返回值:
* The calling conventions for syscalls are as follows: Like ordinary
* function calls, the first 4 32-bit arguments are passed in the 4
* argument registers a0-a3. 64-bit arguments are passed in *aligned*
* pairs of registers, that is, either a0/a1 or a2/a3. This means that
* if the first argument is 32-bit and the second is 64-bit, a1 is
* unused.
*
* This much is the same as the calling conventions for ordinary
* function calls. In addition, the system call number is passed in
* the v0 register.
*
* On successful return, the return value is passed back in the v0
* register, or v0 and v1 if 64-bit. This is also like an ordinary
* function call, and additionally the a3 register is also set to 0 to
* indicate success.
*
* On an error return, the error code is passed back in the v0
* register, and the a3 register is set to 1 to indicate failure.
* (Userlevel code takes care of storing the error code in errno and
* returning the value -1 from the actual userlevel syscall function.
* See src/user/lib/libc/arch/mips/syscalls-mips.S and related files.)
例如你可以看到sys_reboot
被称为tf->tf_a0
,那是因为在发出系统调用之前,寄存器a0
包含系统调用的第一个(也是唯一的)参数。
为了简单起见,我不会深入讨论细节,因为它可能无关紧要。例如,发出系统调用的过程有点复杂,但我只提到了相关的内容。也不会谈论如何从堆栈中获取参数,因为我在这里不需要它。
我应该实施sys_open
系统调用,但我不确定如何知道哪个变体open
函数被称为...
我所拥有的只是系统调用号和寄存器的值,其中包括四个参数寄存器、堆栈指针和其他寄存器。
如何确定我面临的是第一个变体(仅具有两个参数)还是第二个变体(具有 3 个参数),以便我可以采取相应的行为?
一些有用的信息:
系统调用的完整代码是here https://github.com/ops-class/os161/blob/master/kern/arch/mips/syscall/syscall.c#L79.
OS161 的整个存储库是here https://github.com/ops-class/os161.
异常处理程序代码在引导期间加载到内存中。
异常处理程序的代码(发出系统调用时运行的第一个代码)是here https://github.com/ops-class/os161/blob/master/kern/arch/sys161/main/start.S#L131.
异常处理程序是一个名为mips_general_handler
只是调用一个函数common_exception
.
mips_general_handler
is here https://github.com/ops-class/os161/blob/master/kern/arch/mips/locore/exception-mips1.S#L86.
common_exception
is here https://github.com/ops-class/os161/blob/master/kern/arch/mips/locore/exception-mips1.S#L106.
common_exception
将寄存器的所有值推入堆栈,还将指针推入推入值的开头(即传递给正在调用的函数的指针)并调用该函数mips_trap
可以找到here https://github.com/ops-class/os161/blob/master/kern/arch/mips/locore/trap.c#L126.
功能misp_trap
查找异常的原因,如果是系统调用,则调用该函数syscall
这是上面给出的。