For this machine problem you will be adding more system calls to xv6 that:
You will implement the following system calls to support thread creation and management:
int thread_create(void (*tmain)(void *), void *stack, void *arg);
This creates a new thread that starts execution at the function
is called with the argument
arg. The caller is responsible for allocating
malloc, given in the file "umalloc.c") a new user stack before
thread_create and passing the address of the top of this memory
region in as the
stack argument. Returns the thread ID (which is really just
a process ID) of the new thread.
Its implementation will closely mimic that of
fork, except that the newly
created process will share the address space of its parent and will
therefore have automatic access to all of its parent's code and data segments.
int thread_join(void **stack);
This will cause the caller to block until a child thread has terminated, upon
which its ID will be returned and a pointer to its stack placed in
that the latter can be freed). If the caller does not have any child threads,
it will return immediately with
While this is similar to the
wait system call, note that it only operates on
children that share the same address space.
wait, in turn, will need to be
updated so that it only synchronizes on processes created by the caller that
do not share the same address space.
The main thread (i.e., the "original" thread) of a given process can call
thread_join as many times as it invoked
thread_create, so to ensure it
To allow your newly minted threads to coordinate critical sections and safely share data, you will implement the following system calls that expose a mutex lock. The lock will be implemented internally using a spinlock.
int mtx_create(int locked);
typedefa separate type for the lock ID, though it isn't strictly necessary). The lock starts out in the
lockedstate (binary: true or false).
int mtx_lock(int lock_id);
int mtx_unlock(int lock_id);
Note that you can build arbitrarily more sophisticated synchronization mechanisms (e.g., semaphores) in userspace by leveraging this mutex lock.
To test your code, you should write a program named
pctest that implements the
producer/consumer problem by sharing a bounded buffer between a parent and child
(or sibling) threads, using the mutex as a primitive for implementing
synchronization. The bounded buffer itself may be as simple as a fixed sized
array (with global indices to mark the in/out positions), or a (non-thread-safe)
data structure that you implement separately.
Your program should run for sufficiently long to demonstrate concurrency (i.e., the consumer and producer should preempt each other), and you should also output the values as they are produced and consumed so as to demonstrate that they are communicated wholly, and in order. To keep things simple, the values may just be ordinal values; e.g., sequential integer values.
Add this test program (
pctest.c) to your git repository, and be sure to commit
and push it as part of your submission.
As before, start by adding any new source files you created with "
FILENAME", committing your work with "
git commit -am "Your commit message"",
then pushing your commits ot the private shared repository with "