Project 4: xv6 Threads

Important Dates

Questions about the project? Send them to .

Due: Wednesday, 11/09, by whenever.


In this project, you'll be adding real kernel threads to xv6. Sound like fun? Well, it should. Because you know fun is our major goal with these projects.

Specifically, you'll do three things. First, you'll define a new system call to create a kernel thread, called clone() . Then, you'll use clone() to build a little thread library, with a thread_create() call, a thread_join() call, and lock_acquire() and lock_release() functions. Finally, you'll show these things work by writing a test program in which multiple threads are created by the parent, and each adds values to a shared counter. And now, for some details.


Your new syscall should look like this: int clone(void *stack, int size) . It does more or less what fork() does, except for one major difference: instead of making a new address space, it should use the parent's address space (which is thus shared between parent and child). You might also notice a single pointer is passed to the call, and size; this is the location of the child's user stack, which must be allocated before the call to clone is made. Thus, inside clone() , you should make sure that when you return, you are running on this stack, instead of the stack of the parent.

As with fork() , the clone() call returns the pid of the child to the parent, and 0 to the newly-created child thread.

Your thread library will be built on top of this, and just have a simple thread_create(void *(*start_routine)(void*), void *arg) routine. This routine should use clone() to create the child, and then call start_routine() with the argument arg .

Your library will also have a thread_join() routine. This call, made by the parent of the thread, simply waits for a thread that the parent created to exit. In this way, it is quite similar to the wait() system call.

Your thread library should also have a simple spin lock. There should be a type lock_t that one uses to declare a lock, and two routines lock_acquire(lock_t *) and lock_release(lock_t *) , which acquire and release the lock. The spin lock should use x86 atomic exchange to built the spin lock (see the xv6 kernel for an example of something close to what you need to do). One last routine, lock_init(lock_t *) , is used to initialize the lock as need be.

To test your code, you should build a simple program that uses thread_create() to create some number of threads; each thread should, in a loop for a fixed number of times, add one to a shared counter. The counter should be made thread safe using locks of course. At the end, the main thread should call thread_join() (repeatedly, once per child) to wait for all the children to complete; at this point, the main thread should print out the value of the counter.

The command line arguments for this program, called threadtest , are thus:

prompt> threadtest numberOfThreads loopCount

Have fun!

The Code

The code (and associated README) can be found in ~cs537-1/ta/xv6/ . Everything you need to build and run and even debug the kernel is in there, as before.

Might be good to read the xv6 book a bit: Here . Particularly useful to read about how fork() works.

You may also find this book useful: Programming from the Ground Up . Particular attention should be paid to the first few chapters, including the calling convention (i.e., what's on the stack when you make a function call, and how it all is manipulated).


Unlike before, we will be grading this project by examining your code and reading a write-up describing the changes you made, as well running the counter test as described above.

The write-up should be short (a few pages at most) and concise. You should also describe how you tested your code, to convince us (and yourselves) that it works!

Handing It In

Use the p4 directory for your handin. If working as a team of two, please handin the material in ONE directory, with a README that clearly indicates the names and CS logins of the team members.

Turn in a writeup, called p4.pdf , which describes the changes and all the stuff you have done to build and test your kernel.

Turn in all files that you have changed or added (.c and .h files, and possibly a modified Makefile). Thus, we should be able to take your handed in files, add the rest of the source code, and build and run your kernel and any tests you have turned in.