Lab 2: Threads & Semaphores
Operating Systems
Due: Wednesday, October 27th, 12:20 PM
(VIP students have 2 weeks from when you receive it.)
We are using the Nachos educational software which is intended
to provide basic OS functionality that may be extended to enable students
to develop and implement OS components. This handout describes how to set
up your software environment, which files you need to read and understand
for this assignment, the lab 2 assignment, how to turn in this assignment
(as well as others), some programming tips, and sample output for this
assignment.
Getting Started
-
The Nachos software runs only on the DecStation machines in the
EdLab. The machine names are: delux1 through delux9, and deluxa through
deluxj. You should do all your compiling, testing, and executing on these
machines. For information on dialing in remotely to an EdLab machine, see
the EdLab web page.
-
The first step in doing the assignments is to make a copy of the following
directory in your course directory, ~/cs377, as follows:
cd ~/cs377
cp -r /courses/cs300/cs377/cs377/nachos/nachos-3.4 .
If you next type ls, you should see: nachos-3.4, which
contains Nachos source code for each assignment. You will not need the
directory nachos-3.4/doc, and you can delete it from your account
if you wish (using rm -r -f nachos-3.4/doc.
Now change directory to your nachos-3.4/code directory (using
cd
nachos-3.4/code and type make all to compile the nachos software
and place an executable called nachos in each of the subdirectories.
If you have trouble compiling, i.e., the compilation terminates with an
error, please get in touch with the TA, but we do not expect this to happen.
For the assignments you should NEVER need to modify the
machine
directory.
Then you can start doing your assignment. To enable us to find your changes
to the source code, you should surround every piece of code you modify
with:
// start changes
put your changes here
// end changes
Late Policy: Assignments are due at 12:00pm on the due date. Projects
will be accepted up to 4 days after the due date with a 10% penalty for
each day it is submitted late. No project will be accepted after this 4
day period. We will check the time stamp on your files in your directory
to ascertain the time you completed your assignment. For full credit, do
not modify the files after the time mentioned in the assignment handout.
Understanding the Nachos software
In this assignment, we give you part of a working thread system; your job
is to complete it, and then to use it to solve two synchronization problems.
Here are some pointers to overviews of the thread and synchronization
mechanisms in Nachos (these are hot links if you read this on the Web):
After installing the software, your first step is to read and understand
the partial thread system we have written for you. This thread system implements
thread fork, thread completion, along with semaphores for synchronization.
Run the program nachos in the
threads directory for a
simple test of our code. Trace the execution path (by hand) for the simple
test case we provide. (The main program for threads/nachos is in threads/main.cc.
It has been compiled with THREADS defined, but USER_PROGRAM, FILESYS, and
NETWORK undefined.)
When you trace the execution path, it is helpful to keep track
of the state of each thread and which procedures are on each thread's execution
stack. You will notice that when one thread calls SWITCH, another thread
starts running, and the first thing the new thread does is to return from
SWITCH. We realize this comment will seem cryptic to you at this point,
but you will understand threads once you understand why the SWITCH that
gets called is different from the SWITCH that returns. (Note: because gdb
does not understand threads, you will get bizarre results if you try to
trace in gdb across a call to SWITCH.)
The files you need to make sure you understand completely for
this assignment are listed below.
-
main.cc, threadtest.cc (in the threads directory)
-
a simple test of our thread routines.
-
synch.h, synch.cc (in the threads directory)
-
synchronization routines: semaphores, locks, and condition variables.
In addition, you need to use (but not modify) parts of some other
files:
-
interrupt.h (in the machine directory)
-
manage enabling and disabling interrupts as part of the machine emulation.
-
list.h (in the threads directory)
-
generic list management.
-
scheduler.h (in the threads directory)
-
manages the list of threads that are ready to run. You will need to use
the ReadyToRun method to indicate that a thread is ready to run.
-
system.h (in the threads directory)
-
Nachos startup/shutdown routines. You will need to use the variable currentThread
to identify the thread currently running.
-
thread.h (in the threads directory)
-
thread data structures and thread operations such as thread fork, thread
sleep and thread finish.
Properly synchronized code should work no matter what order the scheduler
chooses to run the threads on the ready list. In other words, we should
be able to put a call to Thread::Yield (causing the scheduler
to choose another thread to run) anywhere in your code where interrupts
are enabled without changing the correctness of your code. You will be
asked to write properly synchronized code as part of the later assignments,
so understanding how to do this is crucial to being able to do the project.
To help you test your programs, the code linked in with Nachos
will cause Thread::Yield to be called on your behalf in a repeatable
but unpredictable way. Nachos code is repeatable in that if you call it
repeatedly with the same arguments, it will do exactly the same thing each
time. However, if you invoke ``nachos -rs #'', with a different number
each time, calls to Thread::Yield will be inserted at different
places in the code.
Make sure to run various test cases against your solutions to
these problems; for instance, for part one, create multiple writers and
readers and demonstrate that the output can vary, within certain boundaries.
Warning: in our implementation of threads, each thread is assigned
a small, fixed-size execution stack. This limitation may cause bizarre
problems (such as segmentation faults at strange lines of code) if you
declare large data structures to be automatic variables (e.g., ``int buf[1000];'').
You will probably not notice this during the semester, but if you do, you
may change the size of the stack by modifying the
StackSize defined
in switch.h.
Although the solutions can be written as normal C routines, you
will find organizing your code to be easier if you structure your code
as C++ classes. Also, there should be no busy-waiting in any of your solutions
to this assignment.
The Assignment
For this assignment and all subsequent ones, you may work in groups of
1 or 2.
-
(15 points). Implement locks. We have provided the public interface to
locks in synch.h. You need to define the private data and implement
the interface. Note that it should not take you very much code to implement
locks. You should only need to modify synch.h and synch.cc. You should
not need to modify the public parts of synch.h. (Hint: You may find semaphores
and primitive thread routines useful as building blocks. Look at the Salsa
tutorial for additional hints.)
-
(35 points). Implement reader/writer communication using semaphores and/or
locks. (A solution to the readers and writers problem is described in Silberschatz,
Peterson and Galvin, using semaphores.)
A data object is to be shared among several concurrent processes.
Some of the processes - Readers want only to read the content of the shared
object. When a reader, reads the content of the shared object(a single
character), it should produce the following output to the screen:
Reader <id> reading <character>
where <id> is the unique identifier of the Reader.
The Writer processes want to update (i.e. to read and write) the shared
object (a single character which it obtains from a file called writer_input.txt).
When a writer updates the shared object, it should produce the following
output on the screen:
Writer <id> writing <character>
where <id> is the unique identifier of the writer and
<character>
is the character just written on to the shared object.
Test your solution with multiple readers and writers. Of course, with
multiple readers and writers, the output will illustrate the concurrency
provided by threads.
Your code should prompt for the number of readers and the number of
writers, in that order. You can assume that a valid integer is provided
by the user for each of these. You should be sure that your code works
for different input files as we may test it with files other than what
you provide.
The skeleton code to get you started is in /courses/cs300/cs377/cs377/public_html/labs/writer.cc.
-
(15 points).Condition variables. Implement condition variables.
We have provided the public interface to condition variables in synch.h.
You need to define the private data and implement the interface. Note that
it should not take you very much code to implement condition variables.
You should only need to modify synch.h and synch.cc. You should not need
to modify the public parts of synch.h.
-
(35 points). The Cigarette-Smokers Problem. Implement a solution
to the following problem using locks and condition variables. Consider
a system with three smoker processes and one agent process. Each smoker
continously rolls a cigarette and then smokes it. But to roll and smoke
a cigarette, the smoker needs three ingredients : tobacco, paper and matches.
One of the smoker processes has paper, another has tobacco, and the third
has matches. The agent has an infinite supply of all the three materials.
The agent places any two of the ingredients on the table(randomly). The
smoker who has the remaining ingredient then makes and smokes a cigarette,
signalling the agent on completion. The agent then puts out another two
of the three ingredients, and the cycle repeats. Write a program to synchronize
the smokers and the agent. When an agent places ingredients on the table,
it outputs "Agent putting <item a> and <item b>" where <item
a> and <item b> are the two items selected by the agent. If Smoker A
gets to smoke, it outputs, "Smoker A smoking".
The skeleton code to get you started is in
/courses/cs300/cs377/cs377/public_html/labs/smoker.cc.
How to Turn in Lab 2
All of the following files and hard copies must be turned in to get
full credit for a question.
-
Create a directory in your course directory called
~/cs377/lab2.
Make the directory group writable (chmod g+w ~/cs377/lab2). Put
the following files in this directory.
For part 1 and 3: synch.h, synch.cc
For part 2: reader.cc, reader
For part 4: smoker.cc, smoker
README
To create your solutions for parts 2 and 4, do the following in
the threads directory:
-
Make a copy of threadtest.cc called threadtest_original.cc.
-
Modify threadtest.cc to implement the solution for that part of
the assignment. This requires you delete
SimpleThread, insert
your solution, and replace the body of ThreadTest with code to
run your solution. Be sure to surround your changes with the // Start
changed...// End changed comment.
-
After you have finished editing the code, type gmake depend to
generate a new Makefile.
-
To create an executable, type gmake nachos. The executable will
be named nachos.
-
When you are sure your program works, move threadtest.cc,
threadtest.h,
and nachos to the lab2 directory, renaming them as appropriate
for the part you are working on. (For example, threadtest.cc becomes
reader.cc,
threadtest.h
becomes
reader.h, nachos becomes reader. For the second
part, remember to copy your test file to
writer_input.txt as well.)
-
Make the executables group executable (e.g., chmod g+x writer).
Make the source files and test files group readable (e.g., chmod g+r
smoker.cc). The graders must be able to run and test your programs
in your directory!
-
For each part of the assignment, start with and modify a copy of the
original version of threadtest.cc. This method will prevent
you from needing to modify the makefile.
-
Hand in a hard copy of all the files that you modified.
-
Hand in a hard copy of a README file identifying your lab partner (if you
have one) and containing an outline of what you did for the assignment.
It should also explain and motivate your design choices. Keep it short
and to the point. Your ~/cs377/lab2 directory should
also contain this file.
If your implementation does not work, you should also document the
problems in the README, preferably with your explanation of why it does
not work and how you would solve it if you had more time. Of course,
you should also comment your code. We can't give you credit for something
we don't understand!
-
Hand in a hard copy showing sample output from your programs.
-
Individual Group Assessment (for students working in groups only)
10 percent of your lab grade will come from your participation
in this project as a member of your two person group.
What you need to turn in (each person individually):
Turn in a hard copy of your assessment of the division of labor
in the group in the format shown below. If you give yourself a 50%
and your partner gives you a 50%, you will get the whole 10 points.
If you give your partner a 40% and your partner gives himself or herself
a 40%, he or she will get 8 points. And so on...
|
Work Distribution |
My Name: ABC |
60% |
My Partner: XYZ |
40% |
Tips
-
Use only the delux machines in the edlab to compile your program.
Do not work on the alpha machines. A good way to check this is to type
machine
at the prompt - if the shell responds by saying mips, you are
working on the right machine. (The first room that you enter in the Edlab
contains only Alphas. Walk through this room to the adjoining room, which
contains the DecStations.)
-
Think about what you need to do to synchronize your threads before you
start writing code. Have the strategy clear in your head or on paper before
you write code. This strategy is always the best way. (Design before you
code.)
-
We highly recommend that you write and thoroughly test your code in small
increments to make debugging and troubleshooting as easy as possible. It
is easier to get rid of compile time and runtime errors for one routine
at a time, than it is for all the code that you write at once. In the same
vein, use small easy test cases first and more rigorous tests once the
easy cases work.
-
If you are getting a lot of errors while compiling, get rid of the first
few errors. Doing this often eliminates other error messages you get.
-
When you are certain your submission is ready, run your programs in your
directory as the graders would run them. It is your responsibility to set
up all the files in your ~/cs377/lab2 directory.
-
Remember to set the permissions for the graders.
-
Remember to turn in a hard copy of all the files you create or modify,
sample outputs, and a README file.
-
If you are having trouble with the assignment, please see the TA in office
hours or at the discussion section or send the TA email.
Sample Output
> reader -rs 23232
Please enter the number of readers: 4
Please enter the number of writers: 2
writer 0 incremented integer to 1
writer 0 incremented integer to 2
writer 0 incremented integer to 3
writer 1 incremented integer to 4
reader 0 integer value is 4
reader 0 integer value is 4
writer 0 incremented integer to 5
writer 0 incremented integer to 6
writer 1 incremented integer to 7
reader 0 integer value is 7
reader 0 integer value is 7
reader 1 integer value is 7
reader 1 integer value is 7
reader 1 integer value is 7
writer 0 incremented integer to 8
writer 1 incremented integer to 9
writer 1 incremented integer to 10
reader 0 integer value is 10
reader 0 integer value is 10
reader 1 integer value is 10
writer 0 incremented integer to 11
writer 1 incremented integer to 12
writer 1 incremented integer to 13
writer 1 incremented integer to 14
reader 0 integer value is 14
reader 0 integer value is 14
writer 0 incremented integer to 15
writer 0 incremented integer to 16
reader 0 integer value is 16
reader 0 integer value is 16
reader 0 integer value is 16
reader 1 integer value is 16
^C (to halt)
> smoker -rs 2
Agent putting tobacco and matches
Smoker 2 smoking
Agent putting paper and matches
Smoker 1 smoking
Agent putting paper and matches
Smoker 2 smoking
Agent putting tobacco and paper
Smoker 3 smoking
Agent putting tobacco and matches
Smoker 1 smoking
Agent putting tobacco and paper
Smoker 1 smoking
Agent putting paper and matches
Smoker 3 smoking
Agent putting tobacco and matches
Smoker 2 smoking
Agent putting paper and matches
Smoker 2 smoking
Agent putting paper and matches
Smoker 3 smoking
Agent putting tobacco and matches
Smoker 1 smoking
^C