In this exercise you will modify the xv6 kernel to print out a line for each system call invocation. It is enough to print the name of the system call and the return value; you don't need to print the system call arguments.
When you're done, you should see output like this when booting xv6:
... fork -> 2 exec -> 0 open -> 3 close -> 0 $write -> 1 write -> 1
That's init forking and execing sh, sh making sure only two file descriptors are open, and sh writing the $ prompt. (Note: the output of the shell and the system call trace are intermixed, because the shell uses the write syscall to print its output.)
Hint: modify the syscall() function in kernel/syscall.c.
Run the programs you wrote in the lab and inspect the system call trace. Are there many system calls? Which systems calls correspond to code in the applications you wrote above?
Optional: print the system call arguments.
In this exercise you'll add a feature to xv6 that periodically alerts a process as it uses CPU time. This might be useful for compute-bound processes that want to limit how much CPU time they chew up, or for processes that want to compute but also want to take some periodic action. More generally, you'll be implementing a primitive form of user-level interrupt/fault handlers; you could use something similar to handle page faults in the application, for example.
You should add a new alarm(interval, handler) system call. If an application calls alarm(n, fn), then after every n "ticks" of CPU time that the program consumes, the kernel will cause application function fn to be called. When fn returns, the application will resume where it left off. A tick is a fairly arbitrary unit of time in xv6, determined by how often a hardware timer generates interrupts.
You should put the following example program in user/alarmtest.c:
#include "kernel/param.h" #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" void periodic(); int main(int argc, char *argv[]) { int i; printf(1, "alarmtest starting\n"); alarm(10, periodic); for(i = 0; i < 25*500000; i++){ if((i % 250000) == 0) write(2, ".", 1); } exit(); } void periodic() { printf(1, "alarm!\n"); }The program calls alarm(10, periodic) to ask the kernel to force a call to periodic() every 10 ticks, and then spins for a while. After you have implemented the alarm() system call in the kernel, alarmtest should produce output like this:
$ alarmtest alarmtest starting .....alarm! ....alarm! .....alarm! ......alarm! .....alarm! ....alarm! ....alarm! ......alarm! .....alarm! ...alarm! ...$
(If you only see one "alarm!", try increasing the number of iterations in alarmtest.c by 10x.) Here are some hints:
int alarm(int ticks, void (*handler)());
if(which_dev == 2) ..
In your usertrap, when a process's alarm interval expires, you'll want to cause it to execute its handler. How can you do that?
make CPUS=1 qemu
Optional challenges: 1) Save and restore the caller-saved user registers around the call to handler. 2) Prevent re-entrant calls to the handler -- if a handler hasn't returned yet, don't call it again. 3) Assuming your code doesn't check that tf->esp is valid, implement a security attack on the kernel that exploits your alarm handler calling code.