Jan Fan     About     Archive     Feed     中文博客

Everything About System Calls

Why We Need System Calls?

As we all know, OS provides some APIs for app to invoke, which are called System Calls.

Why not just to provide libraries for processes to use freely?

Let's imagine that if your application is power enough, they can...

Thus system calls are nesessary.

!--more--

Trap

Every time your processes ask for a system call, the system trap from user mode to kernel mode.

Three kinds of TRAPS:

They all require to switch from user mode to kernel mode.

INT

When you call an API like read(), open(), fork(), they request system calls by means of INT, an instruction.

Let's check through what happens when you INT in xv6.

int pid= fork();

fork() is defined as a Macro in xv6. It loads the number SYS_fork of fork() into %eax, then INT 64 (64 is the interrupt vector for system calls)

#define SYS_fork    1
#define T_SYSCALL       64      // system call

Here SYS_xxxx has nothing to do with T_SYSCALL, the former is number standing for each system call, the latter is the interrupt vector. INT T_SYSCALL takes care of all the SYS_xxxx

#define SYSCALL(name) \
    .globl name; \
    name: \
    movl $SYS_ ## name, %eax; \
    int $T_SYSCALL; \
    ret

SYSCALL(fork)

Interrupt Vectors

There are 256 vectors in xv6, and vector64 is the only one that normal process can access.

INT 64 is ok, but not INT 1 in processes' code

# vector table
.data
.globl vectors
vectors:
    .long vector0
    .long vector1
    ...
    .long vector64
    ...
    .long vector255

Following is the Interrupt Vector Handler, which is executed after INT(Of cource, INT will automatically change into kernel stack and save processes's stack and return address for you)

.globl vector64
vector64:
pushl $0
pushl $64
jmp alltraps
  # vectors.S sends all traps here.
.globl alltraps
    ... # Save process registers here
    call trap
    ... # Pop process registers here¬
    iret
void
trap(struct trapframe *tf) // tf is push by alltraps one by one
{
    ...
    syscall();
    ...
}

syscall(void) is where system calls are executed. num is the SYS_fork above.

syscalls[num] contains the address of sys_fork(), the target function that fulfills the mission. Therefore syscalls[num]() calls sys_fork().

static int (*syscalls[])(void) = {
    [SYS_fork]    sys_fork,
    [SYS_exit]    sys_exit,
    ...
};

void
syscall(void)
{
    int num;

    num = proc->tf->eax;
    if(num > 0 && num < NELEM(syscalls) && syscalls[num]) { 
        proc->tf->eax = syscalls[num](); // invoke system call
        cprintf("%s -> %d\n", syscalls_name[num], proc->tf->eax);
    } else {
        cprintf("%d %s: unknown sys call %d\n",
                proc->pid, proc->name, num);
        proc->tf->eax = -1;
    }
}

Sa far we have discussed the whole process that how system calls take place in xv6. Load SYS_fork to %eax, INT T_SYSCALL, and eventually you reach the sys_fork().

Now do you understand what happens when you incoke fork()? If no, go and check the code! ;)

Comments

comments powered by Disqus