4.3 Code: Calling system calls
User programs call library functions in order to make system calls. For example, the shell displays a prompt with this function call (in user/sh.c):
write(2, "$ ", 2);
Here’s the library function, in user/usys.S:
write: li a7, SYS_write ecall ret
The code that the C compiler generates for the function call
loads the three arguments into registers a0,
a1, and a2. Then the write() function
loads the system call number, SYS_write (16), into a7.
The kernel will look at those registers to find out what
system call is intended, and what the arguments are.
The ecall instruction traps from user space
into the kernel and causes
uservec,
usertrap, and then syscall to execute.
At this point, please read kernel/syscall.c, sys_write() in kernel/sysfile.c, and copyout(), copyin(), and copyinstr() in kernel/vm.c.
syscall
(3731)
retrieves the system call number from the saved
a7 in the trapframe
and uses it to index into syscalls
(3706).
For our example,
a7
contains
SYS_write
(3566),
resulting in a call to the system call implementation function
sys_write.
When sys_write returns,
syscall
records its return value in
p->trapframe->a0.
This will cause the original user-space call to
write() to return that value, since the C
calling convention on RISC-V places return values in a0.
System calls conventionally return negative numbers to indicate
errors, and zero or positive numbers for success.