如何在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, 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)
发出syscall时,将调用syscall调度程序. syscall调度程序采用指向trapframe
的指针,该指针包括在发出syscall之前的寄存器值.寄存器之一包含系统调用号,调度程序将其分配给正确的系统调用功能.调度程序看起来像这样:
When a syscall is issued, a syscall dispatcher is called. The syscall dispatcher takes a pointer to a trapframe
which includes the values of registers before the syscall is issued. One of the registers contains the syscall number, which the dispatcher uses to dispatch to the right syscall function. The dispatcher looks like this:
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);
...
}
这是一条注释,描述了如何传递参数以及如何返回值:
Here is a comment that describes how arguments are passed and how to return values:
* 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
包含系统调用的第一个(也是唯一的)参数.
You can see that for example sys_reboot
is called with tf->tf_a0
, That's because before the syscall is issued, the register a0
contained the first (and only) parameter for the syscall.
为了简单起见,我不会深入研究细节,因为这可能无关紧要.例如,发出系统调用的过程要复杂一些,但是我只提到了相关的内容.同样也不会谈论如何从堆栈中获取参数,因为在这里我不需要它.
I'm not gonna dive into details for simplicity and because it's probably irrelevant. For example, the process of issuing a syscall is a bit more complicated, but I've mentioned only the relevant stuff. Also not gonna talk about how to get arguments off the stack since I'll not need it here.
我应该实现sys_open
syscall,但是我不确定如何知道open
函数的哪个变体已被调用...
I'm supposed to implement the sys_open
syscall, but I'm not sure how to know which variant of the open
functions has been called...
我所拥有的只是系统调用号和寄存器的值,其中包括四个参数寄存器,堆栈指针和其他寄存器.
All I have is the syscall number and the values of the registers, which includes the four arguments registers, the stack pointer and the other registers.
如何确定我是面对第一个变化(仅具有两个参数)还是面对第二个变化(具有3个参数),以便我能够表现得相对应?
How can I determine whether I'm facing the first variation (with only two parameters) or the second one (with 3 parameters) so that I can behave accordingly?
整个系统调用代码为 OS161的整个仓库位于此处.
The whole repo for OS161 is here.
在此处引导期间,异常处理程序代码已加载到内存中.
The exception handler code is loaded into memory during the boot here.
The code for the exception handler (the first code that runs when a syscall is issued) is here.
异常处理程序是一个名为mips_general_handler
的函数,它仅调用一个函数common_exception
.
The exception handler is a function called mips_general_handler
that just calls a function common_exception
.
mips_general_handler
是 common_exception
是 common_exception
将寄存器的所有值压入堆栈,还将指针压入压入值的开头(即已传递给被调用函数的指针),然后调用函数mips_trap
在此处找到
common_exception
pushed all the values of the registers onto the stack, also pushes a pointer to the beginning of the pushed values (that's the pointer that's passed to the function being called) and calls the function mips_trap
which can be found here.
函数misp_trap
寻找异常的原因,如果是系统调用,则调用上面给出的函数syscall
.
The function misp_trap
looks for the cause of the exception, and if it's a syscall, calls the function syscall
which is given above.
首先,该OS161手册页错误错误地暗示了open()
函数的两个版本. open()
仅有一个版本-C不支持该手册页所暗示的函数重载. 对于POSIX open()
的一个版本是
First, that OS161 man page is wrong in incorrectly implying that there are two versions of the open()
function. There is only one version of open()
- C does not support function overloading like that man page implies. Per POSIX the one version of open()
is
SYNOPSIS
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int oflag, ...);
请注意该手册页如何使您误以为有open()
的两个版本.没有.在所谓的教学操作系统"中,这种草率的做法确实很糟糕.
Note how that man page confused you into thinking there are two versions of open()
. There isn't. That kind of sloppiness is really bad in what's supposed to be a "teaching operating system".
如果将oflag
参数设置为O_CREAT
位,则会出现mode
参数:
The mode
argument will be present if the oflag
argument as the O_CREAT
bit set:
O_CREAT
如果文件存在,则该标志无效,除非另有说明
O_EXCL
.否则,如果未设置O_DIRECTORY
,则文件应
创建为常规文件;文件的用户标识应设置为
流程的有效用户标识;文件的组ID
设置为文件父目录的组ID或
流程的有效组ID;和访问权限位
(请参见<sys/stat.h>
)文件模式应设置为
oflag参数后面的参数作为类型mode_t
修改如下:对文件模式位执行按位与运算
以及进程文件模式的补码中的相应位
创建蒙版.因此,文件模式下的所有位其对应位
在文件模式下,创建掩码被清除.当其他位
如果设置了文件许可位,则效果未指定.这
oflag参数后面的参数不会影响文件是否
开放供阅读,写作或两者兼而有之.实施应
提供一种将文件的组ID初始化为文件组ID的方法
父目录.实现可以但不必提供
实现定义的方法,用于将文件的组ID初始化为
调用过程的有效组ID.
If the file exists, this flag has no effect except as noted under
O_EXCL
below. Otherwise, if O_DIRECTORY
is not set the file shall
be created as a regular file; the user ID of the file shall be set to
the effective user ID of the process; the group ID of the file shall
be set to the group ID of the file's parent directory or to the
effective group ID of the process; and the access permission bits
(see <sys/stat.h>
) of the file mode shall be set to the value of the
argument following the oflag argument taken as type mode_t
modified as follows: a bitwise AND is performed on the file-mode bits
and the corresponding bits in the complement of the process' file mode
creation mask. Thus, all bits in the file mode whose corresponding bit
in the file mode creation mask is set are cleared. When bits other
than the file permission bits are set, the effect is unspecified. The
argument following the oflag argument does not affect whether the file
is open for reading, writing, or for both. Implementations shall
provide a way to initialize the file's group ID to the group ID of the
parent directory. Implementations may, but need not, provide an
implementation-defined way to initialize the file's group ID to the
effective group ID of the calling process.
假设对于OS161,char *path
参数是64位指针,而int
和mode_t
均为32位,则a0
和a1
寄存器应包含path
指针参数oflag
参数,而a3
应该包含mode
参数,如果oflag
参数中的O_CREAT
位设置了.如果用户进程调用代码未使用mode
参数,但设置了O_CREAT
位,
Assuming for OS161 the char *path
argument is a 64-bit pointer and both int
and mode_t
are 32 bits, the a0
and a1
registers should contain the path
pointer argument, a2
should contain the oflag
argument, and a3
should contain the mode
argument if the O_CREAT
bit is set in the oflag
argument. If the user-process calling code did not use the mode
argument but did set the O_CREAT
bit,
请注意, Linux open()
syscall 正是以这种方式实现的*-mode
参数由调用过程设置(如果相关).
Note that the Linux open()
syscall is implemented in exactly this way* - the mode
argument is set by the calling process if it's relevant.
*-差不多了. Linux实际上将open()
实现为openat( AT_FDCWD, ...)
.如果OS161提供openat()
,则您可能还应该将open()
实施为openat( AT_FDCWD, ...)
.
* - Almost. Linux actually implements open()
as openat( AT_FDCWD, ...)
. If OS161 provides openat()
, you should probably implement open()
as openat( AT_FDCWD, ...)
also.