Project 1b: xv6 Intro
We'll be doing kernel hacking projects in xv6, a port of a classic
version of unix to a modern processor, Intel's x86. It is a clean and
beautiful little kernel.
This first project is just a warmup, and thus relatively light.
The goal of the project is simple: to add one system call to xv6
and create one user-level application that calls it.
The system call is:
The user-level application should behave as follows:
- int getnumsyscallp(void) returns the total number of
system calls that have been issued by the calling process, not
including calls to
getnumsyscallp() itself. The count
should be incremented before a system call is issued, not
after. The system call will simply return the value of a counter
that is associated with the calling process.
You must use the names of the system call and the application exactly as specified!
- syscallptest N. This program takes one
argument, N, which is the number of system calls (excluding getnumsyscallp()) it makes between calls to
Before it calls
exit(), it should print out two values: the value returned
getnumsyscallp() when it is called first
main() and the value returned
getnumsyscallp() after the N system calls have
The source code for xv6 (and associated README) can be found in
~cs537-1/ta/xv6/ . Everything you need to build, run, and even debug the
kernel is in there; start by reading the README.
After you have un-tarred the
xv6.tar.gz file, you can
make qemu-nox to compile all the code and run it
using the QEMU emulator. Test out the unmodified code by running a
few of the existing user-level applications, like
forktest. To quit the emulator, type
Using gdb (the debugger) may be helpful in understanding code.
Look at the Makefile to see how to start up the debugger. Get
familiar with this fine tool!
You will not write many lines of code for this project. Instead, a
lot of your time will be spent learning where different routines are
located in the existing source code. You will end up modifying files
that are mostly in the kernel subdirectory. The primary files
you will want to examine in detail
You may also find the following book about xv6 useful, written by the
same team that ported xv6 to x86:
Particularly useful for this project: Chapters 0 and 3 (and maybe 4). Note
that our version of xv6 is slightly older than the book's, so you may
encounter a difference here and there.
To add a system call, find some other very simple system call,
getpid(), copy it in all the ways you think are
needed, and modify it to havethe name
Compile the code to see if you found everything you need to copy and
Then think about the changes that you will need to make
getnumsyscallp() acts like itself instead
- You need a counter per process. What is the data structure that is associated with each process? Try adding a new field to this structure.
- You need to initialize the counter when the process is first created. Where is a good place to initialize a per-process counter? In xv6, a process is created using the
- You need to increment the counter in the right place. As the video from discussion section described in detail, the
syscall() procedure is where you want to look. Be sure you don't increment the counter if the system call number corresponds to
For this project, you
do not need to worry about concurrency or locking.
You also need to create a user-level application
syscallptest that calls
getnumsyscallp() exactly two times. Again, we suggest copying one of the straight-forward utilities that exist in the
Some things to watch out for:
Good luck! While the xv6 code base might seem intimidating at first, you only need to understand very small portions of it for this project. This project is very doable!
- Calling variants of printf() in your application involves making system calls! Therefore, make sure you save the values returned by
getnumsyscallp() before your program prints out any values!
- You will see that the initial value returned by
getnumsyscallp() is not zero (big hint: it should be TWO). Creating a new process from the shell involves making system calls. If you are curious, you can determine what these system calls are by looking through
sh.c. You will see that one is
exec() and one is
sbrk(), which allocates memory to this new process.
- For invoking a number of system calls equal to the argument N passed to this application, we recommend invoking a simple system call like