Nachos Thread Internals
Tom Kelliher, CS 318
Feb. 2, 1998
Announcements:
From last time:
- Nachos: groups, accounts, code, and compiling.
Outline:
- Internals of Nachos thread code.
- Critical sections and cooperating processes.
Assignment:
- Note the command line switches to Nachos. Demonstrate.
- The first DEBUG message in main() won't ever print
anything. Why?
- Once we leave Initialize, main() is running as a thread.
Complication: main() cannot exit while other threads are active.
- The Makefile in the threads directory sets-up the
compiler command line to define THREADS. Demonstrate.
- Note call to
currentThread->Finish()
.
- Called from main() .
- It forks a thread, which executes SimpleThread() with a
parameter of 1.
- it executes SimpleThread() with a parameter of 0.
- Using
Thread::Yield()
, the threads ping pong back and forth.
- Threads are being scheduled, somehow. How can we construct a test
here to determine the scheduling algorithm?
Declaration of the thread class.
- Note the comments regarding thread stack overflows.
- Try to find defn. of ThreadStackSize.
- Why can't stackTop and machineState be moved?
- Parameters to Thread::Fork() .
- Don't rely on Thread::CheckOverflow() .
- Interface to switch.s .
- Thread::Fork(): Use of interrupt ``mask'' to achieve mutually
exclusive access (atomicity) to shared data:
void
Thread::Fork(VoidFunctionPtr func, int arg)
{
DEBUG('t', "Forking thread \"%s\" with func = 0x%x, arg = %d\n",
name, (int) func, arg);
StackAllocate(func, arg);
IntStatus oldLevel = interrupt->SetLevel(IntOff);
scheduler->ReadyToRun(this); // ReadyToRun assumes that interrupts
// are disabled!
(void) interrupt->SetLevel(oldLevel);
}
Initializes thread and puts on ready queue.
- Thread::Finish(): note that the thread can't be destroyed here.
Another reason why a thread/process can't immediately be removed: zombies
in Unix.
Note use of Sleep() .
- What's the difference between Yield() and Sleep() ?
We need to discuss the critical section problem.
What is a critical section?
The overlapping portion of cooperating processes, where shared
variables are being accessed.
Not all processes share variables: independent processes.
Cooperating/independent processes.
Necessary conditions for a solution to the c.s. problem:
- Mutual Exclusion --- if is executing in one of its critical
sections, no , , is executing in its critical sections.
- Progress --- a process operating outside of its critical section
cannot prevent other processes from entering theirs; processes attempting
to enter their critical sections simultaneously must decide which process
enters eventually.
- Bounded Waiting --- a process attempting to enter its critical
region will be able to do so eventually.
Assumptions:
- No assumptions made about relative speed of processes
- No process may remain in its critical section indefinitely (may not
terminate in its critical section)
- A memory operation (read or write) is atomic --- cannot be
interrupted. For now, we do not assume indivisible RMW cycles.
Classic example: the producer/consumer problem (aka bounded
buffer):
Global data:
const int N = 10;
int buffer[N];
int in = 0;
int out = 0;
int full = 0;
int empty = N;
Producer:
while (1)
{
while (empty == 0)
;
buffer[in] = inData;
in = ++in % N;
--empty;
++full;
}
Consumer:
while (1)
{
while (full == 0)
;
outData = buffer[out];
out = ++out % N;
--full;
++empty;
}
Is there potential for trouble here?
(for n processes, )
Pi:
do {
mutexbegin(); /* CS entry */
CSi;
mutexend(); /* CS exit */
non-CS
} while (!done);
- Just disable interrupts.
- Umm, what about user processes?
- Why this doesn't work with multiprocessors.
- This is dangerous.
Thomas P. Kelliher
Fri Jan 30 11:03:29 EST 1998
Tom Kelliher