Administrivia Administrivia • Nachos guide and Lab #1 are on the web. Threads and Concurrency Threads and Concurrency http://www.cs.duke.edu/~chase/cps210 • Form project teams of 2-3. • Lab #1 due February 5. • Synchronization problem set is up: due January 29. • Synchronization hour exam on January 29. • Readings page is up. • Read Tanenbaum ch 2-3 and Birrell for Thursday’s class. Threads Threads A Peek Inside a Running Program A Peek Inside a Running Program A thread is a schedulable stream of control. 0 CPU defined by CPU register values (PC, SP) common runtime x your program suspend : save register values in memory code library resume : restore registers from memory Multiple threads can execute independently: address space your data R0 (virtual or physical) They can run in parallel on multiple CPUs... heap - physical concurrency Rn …or arbitrarily interleaved on a single CPU. PC x y SP - logical concurrency registers Each thread must have its own stack. y stack high “memory” Two Threads Sharing a CPU Two Threads Sharing a CPU A Program With Two Threads A Program With Two Threads address space “on deck” and 0 concept ready to run common runtime x program code library running thread data R0 reality CPU Rn y PC x stack y SP registers context switch stack high “memory” 1
Thread Context Switch Thread Context Switch Thread States and Transitions Thread States and Transitions switch switch address space in out 0 common runtime running x program Thread::Yield code library (voluntary or involuntary) Thread::Sleep (voluntary) Scheduler::Run data R0 1. save registers Rn CPU blocked ready Scheduler::ReadyToRun y PC x stack (“ wakeup ”) SP y registers 2. load registers stack high “memory” Blocking in Blocking in Sleep Sleep Why Threads Are Important Why Threads Are Important • An executing thread may request some resource or action 1. There are lots of good reasons to use threads. that causes it to block or sleep awaiting some event. “easy” coding of multiple activities in an application passage of a specific amount of time (a pause request) e.g., servers with multiple independent clients completion of I/O to a slow device (e.g., keyboard or disk) parallel programming to reduce execution time release of some needed resource (e.g., memory) 2. Threads are great for experimenting with concurrency. In Nachos, threads block by calling Thread::Sleep. context switches and interleaved executions • A sleeping thread cannot run until the event occurs. race conditions and synchronization • The blocked thread is awakened when the event occurs. can be supported in a library (Nachos) without help from OS E.g., Wakeup or Nachos Scheduler::ReadyToRun(Thread* t) 3. We will use threads to implement processes in Nachos. • In an OS, threads or processes may sleep while executing in (Think of a thread as a process running within the kernel .) the kernel to handle a system call or fault. Concurrency Concurrency CPU Scheduling 101 CPU Scheduling 101 Working with multiple threads (or processes) introduces The CPU scheduler makes a sequence of “moves” that concurrency : several things are happening “at once”. determines the interleaving of threads. How can I know the order in which operations will occur? • Programs use synchronization to prevent “bad moves”. • physical concurrency • …but otherwise scheduling choices appear (to the program) On a multiprocessor , thread executions may be arbitrarily to be nondeterministic . interleaved at the granularity of individual instructions. The scheduler’s moves are dictated by a scheduling policy . • logical concurrency On a uniprocessor , thread executions may be interleaved as the system switches from one thread to another. interrupt current thread blocked or wait for it to threads readyList context switch (suspend/resume) block/yield/terminate Warning : concurrency can cause your programs to behave Wakeup or unpredictably, e.g., crash and burn. GetNextToRun () ReadyToRun SWITCH() 2
Context Switches: Voluntary and Involuntary Context Switches: Voluntary and Involuntary The Dark Side of Concurrency The Dark Side of Concurrency On a uniprocessor , the set of possible execution schedules With interleaved executions, the order in which processes depends on when context switches can occur . execute at runtime is nondeterministic . depends on the exact order and timing of process arrivals • Voluntary : one thread explicitly yields the CPU to another. E.g., a Nachos thread can suspend itself with Thread::Yield . depends on exact timing of asynchronous devices (disk, clock) depends on scheduling policies It may also block to wait for some event with Thread::Sleep . • Involuntary: the system scheduler suspends an active thread, Some schedule interleavings may lead to incorrect behavior. and switches control to a different thread. Open the bay doors before you release the bomb. Thread scheduler tries to share CPU fairly by timeslicing . Two people can’t wash dishes in the same sink at the same time. Suspend/resume at periodic intervals (e.g., nachos -rs ) The system must provide a way to coordinate concurrent Involuntary context switches can happen “any time”. activities to avoid incorrect interleavings. Example: A Concurrent Color Stack Example: A Concurrent Color Stack Interleaving the Color Stack #1 Interleaving the Color Stack #1 InitColorStack() { push(blue); PushColor() { push(purple ); if (s[top] == purple) { } ASSERT(s[top-1] == blue); push(blue); } else { PushColor() { ASSERT(s[top] == blue); if (s[top] == purple) { ASSERT(s[top-1] == purple); ASSERT(s[top-1] == blue); push(purple); push(blue); } } } else { ASSERT(s[top] == blue); ThreadBody() { ASSERT(s[top-1] == purple); while(1) push(purple ); PushColor(); } } } Interleaving the Color Stack #2 Interleaving the Color Stack #2 Interleaving the Color Stack #3 Interleaving the Color Stack #3 Consider a yield here on blue’s first call to PushColor(). if (s[top] == purple) { ASSERT(s[top-1] == blue); push(blue); } else { if (s[top] == purple) { ASSERT(s[top] == blue); ASSERT(s[top-1] == blue); ASSERT(s[top-1] == purple); push(blue); push(purple); } else { } X ASSERT(s[top] == blue); ASSERT(s[top-1] == purple); push(purple); } 3
Interleaving the Color Stack #4 Interleaving the Color Stack #4 Threads vs. Processes Threads vs. Processes 1. The process is a kernel abstraction for an independent executing program. Consider yield here on blue’s first call to data includes at least one “thread of control” PushColor(). also includes a private address space (VAS) - requires OS kernel support if (s[top] == purple) { (but some use process to mean what we call thread) ASSERT(s[top-1] == blue); 2. Threads may share an address space push(blue); } else { threads have “context” just like vanilla processes data ASSERT(s[top] == blue); ASSERT(s[top-1] == purple); - thread context switch vs. process context switch push(purple); every thread must exist within some process VAS } X processes may be “multithreaded” Thread::Fork Kernel Kernel-Supported Threads Supported Threads User User-level Threads level Threads Most newer OS kernels have kernel-supported threads . The Nachos library implements user-level threads . • thread model and scheduling defined by OS • no special support needed from the kernel (use any Unix) Nachos kernel can support them: extra credit in Labs 4 and 5 • thread creation and context switch are fast (no syscall) NT, advanced Unix, etc. • defines its own thread model and scheduling policies Kernel scheduler (not a data library) decides which New kernel system calls, e.g.: thread to run next. thread_fork thread_exit readyList thread_block thread_alert data while(1) { etc... t = get next ready thread; scheduler ->Run(t); Threads can block } independently in Threads must enter the kernel kernel system calls. to block: no blocking in user space A Nachos Thread A Nachos Thread A Nachos Context Switch A Nachos Context Switch t = new Thread(name); Save current stack pointer t->Fork(MyFunc, arg); and caller’s return address in old thread object. currentThread ->Sleep(); /* currentThread ->Yield(); * Save context of the calling thread ( old ), restore registers of * the next thread to run ( new ), and return in context of new . Caller-saved registers (if */ needed) are already saved on the thread’s stack. “fencepost” switch/MIPS (old, new) { Thread* t old ->stackTop = SP; save RA in old ->MachineState[PC]; Caller-saved regs restored low save callee registers in old -> MachineState unused region automatically on return. 0xdeadbeef name/status, etc. restorecalleeregisters from new-> MachineState machine state Stack RA = new ->MachineState[PC]; SP = new ->stackTop; high stack top Switch off of old stack and return (to RA) back to new stack. } thread object intstack[StackSize] or Return to procedure that thread control block called switch in new thread . 4
Recommend
More recommend