Project 6: xv6 Threads

Important Dates

Questions about the project? Send them to 537-help@cs.wisc.edu .

Due: Monday, 4/25, by whenever.

Notes

This project can be done in either by yourself or in a team of size two.

Overview

In this project, you'll be adding real kernel threads to xv6. Sound like fun? Well, it should. Because you are on your way to becoming a real kernel hacker. And what could be more fun than that?

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 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 insert items into a thread-safe linked list that you will write. That's it! And now, for some details.

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 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 an element to a shared linked list of integers. For example, if a thread is told to run for 10 iterations through a loop, it should add an element for 0, then 1, then 2, and so on to the list. If there are two threads running, they should each do this in parallel. The linked list should be thread safe, grabbing a lock each time an element is being added to the list, and letting it go when done. At the end, the main thread should call wait() (repeatedly, once per child) to wait for all the children to complete; at this point, the main thread should add up all of the values of the elements on the list, and print out a final value.

The command line arguments for this program are thus:

prompt> main 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.

You may also find the following readings about xv6 useful, written by the same team that ported xv6 to x86: chapters 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 .

All of these can be found off the xv6 site .

Particularly useful for this project: Chapters 2, 4, 5.

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).

Testing

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 list 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 p6 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. Also, create a soft link to the handin directory of the other team member with the name partner ; for example, if the two partners are named joe and jane, and you turn in the project in jane's p5 directory, you should go into jane's p5 directory and type ln -s ~cs537-1/handin/joe/p5 partner to create the soft link.

Turn in a writeup, called p6.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.