Volatile variables
– volatile is a c keyword that tells the compiler that the
variable is shared by multiple threads, and that therefore
it should be careful when it makes optimizations to the
code.
– For example, it shouldn't store the variable in a register.
– Failure to declare a shared variable “volatile” may result
in a seemingly strange behavior of the variable.
Volatile variables (2)
– Example:
int done;
main (){
done=false;
while (!done);
...
}
– A compiler that tries to optimize the code may use a
register for done
– The register is initially loaded with false
– When “while (!done)” is executed, done is read directly
from the register, not the memory address.
– If another thread changes the value of done in memory,
this thread will not see it.
Volatile variables (3)
Volatile can be useful in these situations:
– Memory-mapped I/O
– Any global variable that multiple threads use
– Variables that will be changed by interrupt service
routines
A1 Q1
What happens to a thread when it exits (i.e., calls thread_exit
)? What about when it sleeps?
A1 Q1
thread_exit
* The magic number on the stack is checked for overflow.
* Interrupts are disabled.
* The address space associated with the thread is cleaned
up.
* The reference count on the cwd decremented.
* Switch away leaving a zombie. Can't clean up a running
context.
A1 Q1
thread_sleep
Yield the cpu to another process, and go to sleep, on "sleep
address" ADDR. Subsequent calls to thread_wakeup with
the same value of ADDR will make the thread runnable
again. The address is not interpreted. Typically it's the
address of a synchronization primitive or data structure.
A1 Q2
What function(s) handle(s) a context switch?
mi_switch(nextstate):
-interrupts are off.
-depending on nextstate (S_READY, S_SLEEP, S_ZOMB),
deals with the current thread.
-calls the scheduler to get the next thread
-calls md_switch(old_pcb, new_pcb)
md_switch: if old=new, just returns.
-saves curkstack and in_interrupt in the old pcb
-sets curkstack and in_interrupt to the ones of the new
thread
-calls mips_switch (old_pcb, new_pcb)
A1 Q2
mips_switch:
-saves registers of old
-saves the stack pointer of old
-gets stack pointer of new
-restores the saved registers for new
-returns: md_switch-> mi_switch
mi_switch:
-exorcises zombies
-activates the address space of the new thread
-returns
A1 Q3
How many thread states are there? What are they?
S_RUN,
S_READY,
S_SLEEP,
S_ZOMB
A1 Q4
What does it mean to turn interrupts off? How is this
accomplished? Why is it important to turn off interrupts in
the thread subsystem code?
Mask hardware (asynchronous) interrupts such that they
are delayed and don't interrupt the current code.
Call splhigh to disable, call splx() to restore previous state
(allows nested calls).
The thread code has no concurrency control as it assumes it
will not be interrupted by an interrupt.
A1 Q5
What happens when a thread wakes up another thread?
How does a sleeping thread get to run again?
Given an address, the routine searches for matching
sleepers and adds them all back into the ready queue.
A1 Q6
What function is responsible for choosing the next thread to
run?
scheduler()
A1 Q7
How does that function pick the next thread?
Removes the head from a single ready queue.
A1 Q8
What role does the hardware timer play in scheduling?
What hardware independent function is called on a timer
interrupt?
It generates a regular tick used to regularly preempt
threads.
The tick calls hardclock.
A1 Q9
Describe how thread_sleep and thread_wakeup are used to
implement semaphores. What is the purpose of the argument
passed to thread_sleep()?
-Proberen (test) calls thread_sleep until a resource is available
-Verhogen (increment) calls thread_wakeup
Note the while loop in P (alls threads that sleep on the
semaphore are woken up when thread_wakeup is called) and
the critical section in the code of P and V.
The argument passed to thread_sleep identifies the semaphore,
this way thread_wakeup knows which threads to wake up.
A1 Q10
Why does the lock API in OS/161 provide lock_do_i_hold,
but not lock_get_holder?
The return value would have little practical use, as the lock
holder may have changed by the time the function calling
lock_get_holder uses the return value.