问题
From the man pages of 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);
How the standard c library function open
is defined:
int open(const char *filename, int flags, ...);
The declaration:
/*
* 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)
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.)
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.
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.
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?
Some useful information:
The whole code for syscall is here.
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.
The exception handler is a function called mips_general_handler
that just calls a function common_exception
.
mips_general_handler
is here.
common_exception
is here.
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.
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.
回答1:
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, ...);
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".
The mode
argument will be present if the oflag
argument as the O_CREAT
bit set:
O_CREAT
If the file exists, this flag has no effect except as noted under
O_EXCL
below. Otherwise, ifO_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 typemode_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.
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,
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.
* - Almost. Linux actually implements open()
as openat( AT_FDCWD, ...)
. If OS161 provides openat()
, you should probably implement open()
as openat( AT_FDCWD, ...)
also.
来源:https://stackoverflow.com/questions/63721977/how-to-add-the-two-variations-of-the-open-syscall-in-os161