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:
- 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.
The user-level application should behave as follows:
- syscallptest N. This program takes one
argument, N, which is the number of system calls (excluding getnumsyscallp()) it makes between calls to
getnumsyscallp() .
Before it calls exit() , it should print out two values: the value returned
by getnumsyscallp() when it is called first
within main() and the value returned
by getnumsyscallp() after the N system calls have
been made.
You must use the names of the system call and the application exactly as specified!
The Code
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
run 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 ls
and forktest . To quit the emulator, type Ctl-a x .
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
include syscall.c , sysproc.c , proc.h ,
and proc.c .
You may also find the following book about xv6 useful, written by the
same team that ported xv6 to x86:
book .
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.
Tips
To add a system call, find some other very simple system call,
like getpid() , copy it in all the ways you think are
needed, and modify it to havethe name getnumsyscallp() .
Compile the code to see if you found everything you need to copy and
change.
Then think about the changes that you will need to make
so getnumsyscallp() acts like itself instead
of getpid() .
- 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
fork() routine.
- 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 getnumsyscallp() !
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 user subdirectory.
Some things to watch out for:
- 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
getpid() .
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!
|