Landon Cox
Applications SW atomic operations
OS
HW atomic operations
Hardware
Sofa capacity = 4
Standing room
Customer room capacity = 9
Enter room
Customer () { lock.acquire (); while (numInRoom == 9) roomCV.wait (lock); numInRoom++; lock; roomCV; numInRoom=0; sofaCV; numOnSofa=0; chairCV; numInChair=0; customerCV; cutCV;
Sit on sofa
Sit in chair while (numOnSofa == 4) sofaCV.wait (lock); numOnSofa++;
Wake up barber
Wait for cut to finish
Leave the shop
} while (numInChair == 3) chairCV.wait (lock); numInChair++; numOnSofa--; sofaCV.signal (lock); customerCV.signal (lock); cutCV.wait (lock); numInChair--; chairCV.signal (lock); numInRoom--; roomCV.signal (lock); lock.release ();
Barber () { lock.acquire (); while (1) { while (numInChair == 0) customerCV.wait (lock); cutHair (); cutCV.signal ();
} lock.release ();
}
Customer () { lock.acquire (); while (numInRoom == 9) roomCV.wait (lock); numInRoom++; while (numOnSofa == 4) sofaCV.wait (lock); numOnSofa++;
} while (numInChair == 3) chairCV.wait (lock); numInChair++; numOnSofa--; sofaCV.signal (lock); customerCV.signal (lock); cutCV.wait (lock); numInChair--; chairCV.signal (lock); numInRoom--; roomCV.signal (lock); lock.release ();
Semaphore room = 9, sofa = 4, chair = 3, customer = 0, cut = 0;
Barber () { while (1) { customer.down()
// cut hair cut.up ()
}
}
Is anything weird here?
Customer () { room.down ()
// enter room sofa.down ()
// sit on sofa chair.down ()
// sit on chair sofa.up () customer.up () cut.down ()
// leave chair chair.up ()
// leave room room.up ()
}
Project 1
Due in 1 weeks
Should be done with disk scheduler
Should be knee-deep in thread library
Extra office hours
Will post/announce tomorrow
Most will be over the weekend
Any questions?
Wrapping up synchronization today
Address spaces up next
In 12 days (February 23)
Rest of lecture: mini-review of threads
running thread
“on deck” and ready to run
0 x address space common runtime program code library data
CPU
R0
Rn
PC
SP x y registers y stack high stack
“memory”
CPU switch out switch in
R0
Rn
PC
SP x y registers
0 x address space common runtime program code library data
1. save registers y stack
2. load registers high stack
“memory”
t = new Thread(name); t->Fork(MyFunc, arg); currentThread->Sleep(); currentThread->Yield();
Thread* t
“fencepost” unused region
0xdeadbeef low
Stack high stack top char stack[StackSize] thread object or thread control block name/status, etc.
machine state
}
/*
* Save context of the calling thread (old), restore registers of
* the next thread to run (new), and return in context of new.
*/
switch/MIPS (old, new) { old->stackTop = SP; save RA in old->MachineState[PC]; save callee registers in old->MachineState restore callee registers from new->MachineState
RA = new->MachineState[PC];
SP = new->stackTop; return (to RA)
Save current stack pointer and caller’s return address in old thread object.
}
/*
* Save context of the calling thread (old), restore registers of
* the next thread to run (new), and return in context of new.
*/
switch/MIPS (old, new) { old->stackTop = SP; save RA in old->MachineState[PC]; save callee registers in old->MachineState restore callee registers from new->MachineState
RA = new->MachineState[PC];
SP = new->stackTop; return (to RA)
Caller-saved registers (if needed) are already saved on the thread’s stack.
Caller-saved regs restored automatically on return.
Switch off of old stack and back to new stack.
Return to procedure that called switch in new thread.
Thread::Sleep
(voluntary) running
Scheduler::Run
Thread::Yield
(voluntary or involuntary) blocked ready
“wakeup”
A process is an abstraction
“Independent executing program”
Includes at least one “thread of control”
Also has a private address space (VAS)
Requires OS kernel support
To be covered in upcoming lectures data data
Threads may share an address space
Have “context” just like vanilla processes
Exist within some process VAS
Processes may be “multithreaded”
Key difference
thread context switch vs. process context switch
Project 2: manage process context switches data data
Threads
Address space
Another performance!
What is a process?
Threads
Address space
Ensure mutual exclusion in critical sections.
A lock is an object, a data item in memory.
Threads pair calls to Acquire and Release.
A
Acquire before entering a critical section.
Release after leaving a critical section.
R
Between Acquire/Release, the lock is held.
Acquire doesn’t return until previous holder releases.
Waiting locks can spin (a spinlock) or block (a mutex).
A
R
Who can explain this figure?
R
A
A R
R2
R1
A1
A2
???
A1 A2 R2 R1
Who can explain this figure?
2
X
Y
1
Most OS kernels have kernel-supported threads.
Thread model and scheduling defined by OS
NT, advanced Unix, etc.
Linux: threads are “lightweight processes”
New kernel system calls, e.g.: thread_fork thread_exit thread_block thread_alert etc...
Threads must enter the kernel to block: no blocking in user space data
Kernel scheduler (not a library) decides which thread to run next.
Threads can block independently in kernel system calls.
Can also implement user-level threads in a library.
No special support needed from the kernel (use any Unix)
Thread creation and context switch are fast (no syscall)
Defines its own thread model and scheduling policies
Kernel only sees a single process
Project 1t readyList data
} while(1) { t = get next ready thread; scheduler->Run(t);
Thread
PC
SP …
Thread
PC
SP …
What is “kernel mode?”
Mode the OS executes in
Heightened privileges
Will cover later
Scheduler
Thread
PC
SP …
Thread
PC
SP …
User mode
Kernel mode
Thread
PC
SP …
Thread
PC
SP …
Thread
PC
SP
Sched
…
Scheduler
Thread
PC
SP …
User mode
Kernel mode
Kernel mode
Which do you expect to perform better?
User-threads should perform better
Synchronization functions are just a function call
Attractive in low-contention scenarios
Don’t want to kernel trap on every lock acquire
What is the danger of user-level threads?
If a user thread blocks, entire process blocks
May be hard to take advantage of multiple CPUs
Asynchronous process-kernel interactions
Process never blocks in kernel
Library sleeps thread before entering kernel
If IO, etc ready, library handles interrupt
Restarts thread with correct syscall results
Still not ideal for multi-core
Idea: want to create kernel thread for each core
Let user library manage multiple kernel threads
Comparing user-level threads, kernel threads, and processes.
Operation
Null fork
Signal-wait
FastThreads
34
37
Topaz
Threads
948
441
Ultrix
Processes
11300
1840
Procedure call takes 7 microseconds.
Kernel trap takes 19 microseconds.
Maybe kernel trap is not so significant.
We now understand threads
Each has it a private stack, registers, TCB
Next, we’ll try to understand processes
Address spaces differentiate processes
Address spaces are tied to memory, not CPU
Can think of as a private namespace