Introduction to Xv6: how system call works

In this post, you will learn a few basic concepts of xv6. The learning path will be closely coupled with the first project assignment I gave when I assisted in teaching OS classes.
Understanding system calls and knowing how to implement a simple one will be covered in the first half.
In the second half of this post, I will discuss a little bit more on how to debug xv6 using gdb.

Xv6 System call

To invoke a system call, we have to first define a user-mode function to be the interface of the kernel instruction in file user.h.

void function (void);

This interface-like function will then pass the function name, in this case function, to usys.S. When using user mode function in programs, usys.S will generate a reference to SYS_function and push system call number of this function into %eax. After that, system can know from syscall.c and determining whether this system call is available. We must define same name system function and add it into syscall.h and syscall.c.

#define SYS_function ##  // ## is the system call number
[SYS_function]  sys_function // real system function name
extern int sys_function(void); // real system function declaration

After adding these sentences to syscall files, we can implement real function in specific place where you want to make the function works well.

Sometimes, we need to pass variables among system calls. In this case, variables’ values are not necessary and even can’t be pass directly into system_function. When invoke a system call function, all variables of this system call will be pushed into current process’ stack. In file syscall.c, multiple functions are provided to get these variables from the process. I won’t waste time on explaining how to use these functions especially when elegant and detailed comments were written in source codes. However, I will explain concepts and how process organized and works in xv6 in future articles.

Debug xv6 with gdb

Please make sure that you have used gdb before.
If you never used gdb, you may write a simple 50-100 lines c code and practice how to use gdb first.

To make sure xv6 gdb enabled, please check if .gdbinit.tmpl file exist.
This file is used for generate .gdbinit file which you can late consider it as a configuration for gdb.

Before running the xv6 instance in QEMU, one more thing you need to know is that using gdb to debug xv6 must be attached remotely.
This is because xv6 was running within QEMU, and emulator is virtually gapped from the host device.
Later when you start debugging, QEMU will open a gdb server to let gdb client connect to.

Once you want to start, use following command to compile and run xv6

$ make qemu-nox-gdb
*** Now run 'gdb'.
qemu-system-i386 -nographic -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp 2 7

At this moment, it feels xv6 was stuck, this is because QEMU is ready to be connected by the gdb client.
You may use the .gdbinit to automatically finish this remote connection by simply typing in the following command in another terminal.

$ gdb -x .gdbinit
GNU gdb (Debian 8.2.1-2+b3) 8.2.1


The target architecture is assumed to be i8086
[f000:fff0]    0xffff0: ljmp   $0x3630,$0xf000e05b
0x0000fff0 in ?? ()
+ symbol-file kernel
warning: A handler for the OS ABI "GNU/Linux" is not built into this configuration
of GDB.  Attempting to continue with the default i8086 settings.


Now within this gdb client shell, type ‘c’ to continue the xv6, and you will see xv6 start execution in the first terminal.

At this moment, you may add breakpoints to your code to see if your code is correctly implemented or not.

One more thing, if you open .gdbinit file, you’ll find that it by default connect to a localhost target.
If you are working on some other environment where the target and the client were not placed in the same device, change the localhost to IP address correspondingly.
Using ssh may connect to different physical devices under the same domain name, this is because load balancers were used. To check the IP address, search command IP.

target remote localhost:28467
# target remote [ip-addr]:28467

Further readings

Post regarding how Xv6 scheduler works from kernel bootstrap to first user process


Leave a Reply

Your email address will not be published. Required fields are marked *