如何在OS161中添加开放式系统调用的两个变体?

问题描述

摘自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_reboottf->tf_a0调用,这是因为在发出系统调用之前,寄存器a0包含系统调用的第一个(也是唯一的)参数。

为了简单起见,我不想深入研究细节,因为它可能无关紧要。例如,发出系统调用的过程要复杂一些,但是我只提到了相关的内容。同样也不会谈论如何从堆栈中获取参数,因为在这里我不需要它。


我应该实现sys_open系统调用,但是我不确定如何知道open函数的哪个变体已被调用...

我所拥有的只是系统调用号和寄存器的值,其中包括四个参数寄存器,堆栈指针和其他寄存器。

如何确定我是面对第一个变化(仅具有两个参数)还是面对第二个变化(具有3个参数),以便我能够表现得相对应?


一些有用的信息:

系统调用的整个代码here

OS161的整个存储库为here

在此处引导期间,异常处理程序代码已加载到内存中。

异常处理程序的代码(发出系统调用时运行的第一个代码)为here

异常处理程序是一个名为mips_general_handler函数,它仅调用一个函数common_exception

mips_general_handlerhere

common_exceptionhere

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位指针,而intmode_t都是32位,则a0a1寄存器应如果设置了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,...)