CPS110: Implementing threads on a uni-processor January 29, 2008

advertisement
CPS110:
Implementing threads on a
uni-processor
Landon Cox
January 29, 2008
Recap and looking ahead
Applications
Where
we’ve been
Hardware
Threads,
synchronization
primitives
OS
Atomic Load-Store,
Interrupt enabledisable,
Atomic Test-Set
Where we’re
going
Recall, thread interactions
1. Threads can access shared data
 Use locks, monitors, and semaphores
 What we’ve done so far
2. Threads also share hardware
 CPU (uni-processor)
 Memory
Hardware, OS interfaces
Thread lectures up to this point
Applications
Job 1
Job 2
Job 3
CPU, Mem
CPU, Mem
CPU, Mem
Hardware
OS
Memory
CPU
Memory lectures
Remaining thread lectures
The play analogy
 Process is like a play performance
 Program is like the play’s script
 One CPU is like a one-man-show
 (actor switches between roles)
Threads
Address space
Threads that aren’t running
 What is a non-running thread?
 thread=“sequence of executing instructions”
 non-running thread=“paused execution”
 Must save thread’s private state
 To re-run, re-load private state
 Want thread to start where it left off
Private vs global thread state
 What state is private to each thread?
 Code (like lines of a play)
 PC (where actor is in his/her script)
 Stack, SP (actor’s mindset)
 What state is shared?
 Global variables, heap
 (props on set)
Thread control block (TCB)
 What needs to access threads’ private data?
 The CPU
 This info is stored in the PC, SP, other registers
 The OS needs pointers to non-running threads’ data
 Thread control block (TCB)
 Container for non-running thread’s private data
 Values of PC, code, SP, stack, registers
Thread control block
Address Space
TCB1
PC
Ready
queue
SP
TCB2
TCB3
registers
PC
SP
registers
PC
SP
registers
Code
Code
Code
Stack
Stack
Stack
Thread 1
running
PC
SP
registers
CPU
Thread control block
Address Space
Ready
queue
TCB2
TCB3
PC
SP
registers
PC
SP
registers
Stack
Stack
Code
Stack
Thread 1
running
PC
SP
registers
CPU
Thread states
 Running
 Currently using the CPU
 Ready
 Ready to run, but waiting for the CPU
 Blocked
 Stuck in lock (), wait () or down ()
Switching threads
 What needs to happen to switch threads?
1. Thread returns control to OS
 For example, via the “yield” call
2. OS chooses next thread to run
3. OS saves state of current thread
 To its thread control block
4. OS loads context of next thread
 From its thread control block
5. Run the next thread
Project 1:
swapcontext
1. Thread returns control to OS
 How does the thread system get control?
 Voluntary internal events
 Thread might block inside lock or wait
 Thread might call into kernel for service
 (system call)
 Thread might call yield
 Are internal events enough?
1. Thread returns control to OS
 Involuntary external events
 (events not initiated by the thread)
 Hardware interrupts
 Transfer control directly to OS interrupt handlers
 From 104
 CPU checks for interrupts while executing
 Jumps to OS code with interrupt mask set
 Interrupts lead to pre-emption (a forced yield)
 Common interrupt: timer interrupt
2. Choosing the next thread
 If no ready threads, just spin
 Modern CPUs: execute a “halt” instruction
 Project 1: exit if no ready threads
 Loop switches to thread if one is ready
 Many ways to prioritize ready threads
 Will discuss a little later in the semester
3. Saving state of current thread
 What needs to be saved?
 Registers, PC, SP
 What makes this tricky?
 Self-referential sequence of actions
 Need registers to save state
 But you’re trying to save all the registers
 Saving the PC is particularly tricky
Saving the PC
 Why won’t this work?
Instruction
address
100 store PC in TCB
101 switch to next thread
 Returning thread will execute instruction at 100
 And just re-execute the switch
 Really want to save address 102
4. OS loads the next thread
 Where is the thread’s state/context?
 Thread control block (in memory)
 How to load the registers?
 Use load instructions to grab from memory
 How to load the stack?
 Stack is already in memory, load SP
5. OS runs the next thread
 How to resume thread’s execution?
 Jump to the saved PC
 On whose stack are these steps running?
or Who jumps to the saved PC?
 The thread that called yield
 (or was interrupted or called lock/wait)
 How does this thread run again?
 Some other thread must switch to it
Example thread switching
Thread 1
print “start thread 1”
yield ()
print “end thread 1”
Thread 2
print “start thread 2”
yield ()
print end thread 2”
yield ()
print “start yield (thread %d)”
switch to next thread (swapcontext)
print “end yield (thread %d)”
Thread 1 output
Thread 2 output
-------------------------------------------start thread 1
start yield (thread 1)
start thread 2
start yield (thread 2)
end yield (thread 1)
end thread 1
end yield (thread 2)
end thread 2
Note: this assumes no pre-emptions.
Programmers must assume pre-emptions, so this
output is not guaranteed.
Thread states
Running
Thread is
scheduled
Ready
Thread is
Pre-empted
(or yields)
?
Another
thread calls
unlock or
signal (or I/O
completes)
Thread calls
Lock or wait
(or makes I/O request)
Blocked
Creating a new thread
 Also called “forking” a thread
 Idea: create initial state, put on ready queue
1. Allocate, initialize a new TCB
2. Allocate a new stack
3. Make it look like thread was going to call a function




4.
PC points to first instruction in function
SP points to new stack
Stack contains arguments passed to function
Project 1: use makecontext
Add thread to ready queue
Course administration
 Second UTA
 Jason Lee
 Office hours: Thursday, 4:30-6:30, Teer lab
 Lots of questions on newsgroup
 Great resource
Course administration
 Project 1 disk scheduler (1d)
 How to use concurrency primitives
 Disk scheduler should be done or almost done
 Two/seven groups are done with 1d
 Project 1 thread library (1t)




How concurrency primitives actually work
Best way to learn something is to build it! (no such thing as magic)
Should be able to start after class
Time management is really, really important (start soon!!)
 Happy answer any Project 1 questions
Creating a new thread
Parent
call
return
Parent
thread_create
(parent work)
Child
(child work)
Thread join
 How can the parent wait for child to finish?
Parent
thread_create
(parent work)
Child
(child work)
join
Thread join
 Will this work?
 Sometimes, assuming
 Uni-processor
 No pre-emptions
 Child runs after parent
child () {
print “child works”
}
parent () {
create child thread
print “parent works”
yield ()
print “parent continues”
}
 Never, ever assume these things!
 Yield is like slowing the CPU
 Program must work +- any yields
parent works
child works
parent continues
child works
parent works
parent continues
Thread join
 Will this work?
parent () {
create child thread
lock
print “parent works”
wait
print “parent continues”
unlock
}
1
3
2
child () {
lock
print “child works”
signal
unlock
}
 No. Child can call signal first.
 Would this work with semaphores?
 Yes
 No missed signals (increment sem value)
parent works
child works
parent continues
child works
parent works
parent continues
How can we solve this?
 Pair off for a couple of minutes
parent () {
childDone = 0
create child thread
print “parent works”
lock
while (!childDone) {
wait
}
print “parent continues”
unlock
}
child () {
print “child works”
lock
childDone = 1
signal
unlock
}
parent works
child works
parent continues
child works
parent works
parent continues
Thread join
 Many libraries supply “join” operation
 Makes this a lot easier
parent () {
childDone = 0
create child thread
print “parent works”
join with child
print “parent continues”
}
child () {
print “child works”
}
Download