问题描述
摘自OS161
的手册页:
简介
#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,...);
声明:
/*
* 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)
发出syscall时,将调用syscall调度程序。 syscall调度程序采用指向trapframe
的指针,该指针包含在发出syscall之前的寄存器值。寄存器之一包含系统调用号,调度程序将其分配给正确的系统调用功能。调度程序看起来像这样:
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个参数),以便我能够表现得相对应?
一些有用的信息:
OS161的整个存储库为here。
在此处引导期间,异常处理程序代码已加载到内存中。
异常处理程序的代码(发出系统调用时运行的第一个代码)为here。
异常处理程序是一个名为mips_general_handler
的函数,它仅调用一个函数common_exception
。
mips_general_handler
是here。
common_exception
是here。
common_exception
将寄存器的所有值压入堆栈,还将指针压入压入值的开头(这是传递给被调用函数的指针),然后调用函数{{1} },可以在here中找到。
函数mips_trap
寻找异常的原因,如果是系统调用,则调用上面给出的函数misp_trap
。
解决方法
首先,该OS161手册页错误错误地表示存在open()
函数的两个版本。 open()
只有一个版本-C不支持该手册页所暗示的函数重载。 Per POSIX的一个版本open()
是
SYNOPSIS
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path,int oflag,...);
请注意该手册页如何使您误以为open()
有两个版本。没有。在所谓的“教学操作系统”中,这种草率的做法确实很糟糕。
如果将mode
参数设置为oflag
位,则会出现O_CREAT
参数:
O_CREAT
如果文件存在,则此标志无效,除非如下所述
O_EXCL
。否则,如果未设置O_DIRECTORY
,则文件应 创建为常规文件;文件的用户标识应设置为 流程的有效用户标识;文件的组ID 设置为文件父目录的组ID或 流程的有效组ID;和访问权限位 (请参见<sys/stat.h>
)的文件模式应设置为 oflag参数后面的参数作为类型mode_t
修改如下:对文件模式位执行按位与运算 以及进程文件模式补码中的相应位 创建蒙版。因此,文件模式下的所有位其对应位 在文件模式下,设置创建掩码被清除。当其他位 如果没有设置文件许可位,则效果未指定。的 oflag参数后面的参数不会影响文件 开放供阅读,写作或两者兼而有之。实施应 提供一种方法来将文件的组ID初始化为 父目录。实现可以但不必提供 实现定义的方法,用于将文件的组ID初始化为 调用过程的有效组ID。
假设对于OS161,char *path
参数是64位指针,而int
和mode_t
都是32位,则a0
和a1
寄存器应如果设置了path
位,则包含a2
指针参数,oflag
应该包含a3
参数,并且mode
应该包含O_CREAT
参数在oflag
参数中。如果用户进程调用代码未使用mode
参数而是设置了O_CREAT
位,则
请注意,the Linux open()
syscall正是以这种方式实现的*-mode
参数由调用过程设置(如果相关)。
*-差不多。 Linux实际上将open()
实现为openat( AT_FDCWD,...)
。如果OS161提供了openat()
,则您可能还应该将open()
实施为openat( AT_FDCWD,...)
。