COSC 3407: Operating Systems Lecture 7: Implementing Mutual Exclusion.

Slides:



Advertisements
Similar presentations
PROCESS SYNCHRONIZATION READINGS: CHAPTER 5. ISSUES IN COOPERING PROCESSES AND THREADS – DATA SHARING Shared Memory Two or more processes share a part.
Advertisements

Ch. 7 Process Synchronization (1/2) I Background F Producer - Consumer process :  Compiler, Assembler, Loader, · · · · · · F Bounded buffer.
Chapter 6: Process Synchronization
Background Concurrent access to shared data can lead to inconsistencies Maintaining data consistency among cooperating processes is critical What is wrong.
Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8 th Edition, Chapter 6: Process Synchronization.
Process Synchronization. Module 6: Process Synchronization Background The Critical-Section Problem Peterson’s Solution Synchronization Hardware Semaphores.
CS 162 Discussion Section Week 3. Who am I? Mosharaf Chowdhury Office 651 Soda 4-5PM.
CS444/CS544 Operating Systems Synchronization 2/21/2006 Prof. Searleman
Silberschatz, Galvin and Gagne  Operating System Concepts Chapter 7: Process Synchronization Background The Critical-Section Problem Synchronization.
Avishai Wool lecture Introduction to Systems Programming Lecture 4 Inter-Process / Inter-Thread Communication.
Chapter 6: Process Synchronization. Outline Background Critical-Section Problem Peterson’s Solution Synchronization Hardware Semaphores Classic Problems.
Chapter 6: Process Synchronization. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts – 7 th Edition, Feb 8, 2005 Objectives Understand.
02/23/2004CSCI 315 Operating Systems Design1 Process Synchronization Notice: The slides for this lecture have been largely based on those accompanying.
02/17/2010CSCI 315 Operating Systems Design1 Process Synchronization Notice: The slides for this lecture have been largely based on those accompanying.
Chapter 6: Synchronization. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Principles Module 6: Synchronization 6.1 Background 6.2 The Critical-Section.
CPS110: Implementing threads/locks on a uni-processor Landon Cox.
Synchronization Solutions
U NIVERSITY OF M ASSACHUSETTS, A MHERST Department of Computer Science Emery Berger University of Massachusetts, Amherst Operating Systems CMPSCI 377 Lecture.
Instructor: Umar KalimNUST Institute of Information Technology Operating Systems Process Synchronization.
Computer Science 162 Discussion Section Week 3. Agenda Project 1 released! Locks, Semaphores, and condition variables Producer-consumer – Example (locks,
Synchronization CSCI 444/544 Operating Systems Fall 2008.
02/19/2007CSCI 315 Operating Systems Design1 Process Synchronization Notice: The slides for this lecture have been largely based on those accompanying.
Threads and Critical Sections Vivek Pai / Kai Li Princeton University.
1 Race Conditions/Mutual Exclusion Segment of code of a process where a shared resource is accessed (changing global variables, writing files etc) is called.
Operating Systems CSE 411 CPU Management Oct Lecture 13 Instructor: Bhuvan Urgaonkar.
Implementing Synchronization. Synchronization 101 Synchronization constrains the set of possible interleavings: Threads “agree” to stay out of each other’s.
Process Synchronization Continued 7.2 Critical-Section Problem 7.3 Synchronization Hardware 7.4 Semaphores.
Operating Systems ECE344 Ashvin Goel ECE University of Toronto Mutual Exclusion.
Silberschatz, Galvin and Gagne  Operating System Concepts Chapter 7: Process Synchronization Background The Critical-Section Problem Synchronization.
Chap 6 Synchronization. Background Concurrent access to shared data may result in data inconsistency Maintaining data consistency requires mechanisms.
Chapter 6: Synchronization. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Synchronization Background The Critical-Section.
Silberschatz, Galvin and Gagne ©2013 Operating System Concepts Essentials – 9 th Edition Chapter 5: Process Synchronization.
Chapter 6 – Process Synchronisation (Pgs 225 – 267)
CS 3204 Operating Systems Godmar Back Lecture 7. 12/12/2015CS 3204 Fall Announcements Project 1 due on Sep 29, 11:59pm Reading: –Read carefully.
Chapter 6: Process Synchronization. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Background The.
Chapter 6: Process Synchronization. Module 6: Process Synchronization Background The Critical-Section Problem Peterson’s Solution Synchronization Hardware.
CS399 New Beginnings Jonathan Walpole. 2 Concurrent Programming & Synchronization Primitives.
CSCI-375 Operating Systems Lecture Note: Many slides and/or pictures in the following are adapted from: slides ©2005 Silberschatz, Galvin, and Gagne Some.
Computer Architecture and Operating Systems CS 3230: Operating System Section Lecture OS-5 Process Synchronization Department of Computer Science and Software.
Operating Systems CSE 411 CPU Management Dec Lecture Instructor: Bhuvan Urgaonkar.
U NIVERSITY OF M ASSACHUSETTS A MHERST Department of Computer Science Computer Systems Principles Synchronization Emery Berger and Mark Corner University.
Synchronization CSCI 3753 Operating Systems Spring 2005 Prof. Rick Han.
Process Synchronization CS 360. Slide 2 CS 360, WSU Vancouver Process Synchronization Background The Critical-Section Problem Synchronization Hardware.
Implementing Lock. From the Previous Lecture  The “too much milk” example shows that writing concurrent programs directly with load and store instructions.
1 Critical Section Problem CIS 450 Winter 2003 Professor Jinhua Guo.
Silberschatz, Galvin and Gagne ©2009 Operating System Concepts – 8 th Edition Chapter 6: Process Synchronization.
Implementing Mutual Exclusion Andy Wang Operating Systems COP 4610 / CGS 5765.
Mutual Exclusion -- Addendum. Mutual Exclusion in Critical Sections.
Chapter 6 Synchronization Dr. Yingwu Zhu. The Problem with Concurrent Execution Concurrent processes (& threads) often access shared data and resources.
CS162 Operating Systems and Systems Programming Lecture 4 Synchronization, Atomic operations, Locks, Semaphores September 12, 2011 Anthony D. Joseph and.
CS703 – Advanced Operating Systems
Background on the need for Synchronization
Chapter 5: Process Synchronization
CSCI 511 Operating Systems Chapter 5 (Part B) Mutual Exclusion, Semaphores Dr. Frank Li.
January 31, 2011 Ion Stoica CS162 Operating Systems and Systems Programming Lecture 4 Synchronization, Atomic operations,
Topic 6 (Textbook - Chapter 5) Process Synchronization
Lecture 2 Part 2 Process Synchronization
Critical section problem
Implementing Mutual Exclusion
Chapter 6: Process Synchronization
February 6, 2013 Ion Stoica CS162 Operating Systems and Systems Programming Lecture 5 Semaphores, Conditional Variables.
Implementing Mutual Exclusion
September 12, 2012 Ion Stoica CS162 Operating Systems and Systems Programming Lecture 5 Semaphores, Conditional Variables.
CSE 451: Operating Systems Autumn 2003 Lecture 7 Synchronization
CSE 451: Operating Systems Autumn 2005 Lecture 7 Synchronization
CSE 451: Operating Systems Winter 2003 Lecture 7 Synchronization
CSE 153 Design of Operating Systems Winter 19
CS333 Intro to Operating Systems
Chapter 6: Synchronization Tools
Process/Thread Synchronization (Part 2)
Presentation transcript:

COSC 3407: Operating Systems Lecture 7: Implementing Mutual Exclusion

This lecture… u Hardware support for synchronization u Building higher-level synchronization programming abstractions on top of the hardware support. – Semaphores

The Big Picture u The abstraction of threads is good, but concurrent threads sharing state is still too complicated u Implementing a concurrent program directly with loads and stores would be tricky and error-prone. u So we’d like to provide a synchronization abstraction that hides/manages most of the complexity and puts the burden of coordinating multiple activities on the OS instead of the programmer – Give the programmer higher level operations, such as locks.

Ways of implementing locks u All require some level of hardware support. u Directly implement locks and context switches in hardware – Makes hardware slow! One has to be careful not to slow down the common case in order to speed up a special case. Concurrent Programs High level atomic operations (API) Low level atomic operations (hardware) Locks semaphores monitors send&receive Load/store interrupt disable test&set comp&swap

Disable interrupts (uniprocessor only) u Two ways for dispatcher to get control: – internal events – thread does something to relinquish the CPU – external events – interrupts cause dispatcher to take CPU away u On a uniprocessor, an operation will be atomic as long as a context switch does not occur in the middle of the operation. u Need to prevent both internal and external events. u Preventing internal events is easy (although virtual memory makes it a bit tricky). u Prevent external events by disabling interrupts, in effect, telling the hardware to delay handling of external events until after we’re done with the atomic operation.

A flawed, but very simple solution u Why not do the following: 1. Need to support synchronization operations in user-level code. Kernel can’t allow user code to get control with interrupts disabled (might never give CPU back!). 2. Real-time systems need to guarantee how long it takes to respond to interrupts, but critical sections can be arbitrarily long. Thus, one should leave interrupts off for shortest time possible. 3. Simple solution might work for locks, but wouldn’t work for more complex primitives, such as semaphores or condition variables. Lock::Acquire() { disable interrupts;} Lock::Release() { enable interrupts;}

Implementing locks by disabling interrupts u Key idea: maintain a lock variable and impose mutual exclusion only on the operations of testing and setting that variable. class Lock { int value = FREE; } Lock::Acquire() { Disable interrupts; if (value == BUSY) { Put on queue of threads waiting for lock Go to sleep // Enable interrupts? See comments in next slides } else { value = BUSY; } Enable interrupts; } Enable Position

Implementing locks by disabling interrupts u Why do we need to disable interrupts at all? u Otherwise, one thread could be trying to acquire the lock, and could get interrupted between checking and setting the lock value, so two threads could think that they both have the lock. Lock::Release() { Disable interrupts; If anyone on wait queue { Take a waiting thread off wait queue Put it at the front of the ready queue } else { value = FREE; } Enable interrupts; }

Implementing locks by disabling interrupts u By disabling interrupts, the check and set operations occur without any other thread having the chance to execute in the middle. u When does Acquire re-enable interrupts in going to sleep? u Before putting the thread on the wait queue? – Then Release can check the queue, and not wake the thread up. u After putting the thread on the wait queue, but before going to sleep? – Then Release puts the thread on the ready queue, but the thread still thinks it needs to go to sleep! – goes to sleep, missing the wakeup from Release, and still holds lock (deadlock!) u Want to put it after sleep(). But – how?

Implementing locks by disabling interrupts u To fix this, in Nachos, interrupts are disabled when you call Thread::Sleep; it is the responsibility of the next thread to run to re-enable interrupts. u When the sleeping thread wakes up, it returns from Thread::Sleep back to Acquire. Interrupts are still disabled, so turn on interrupts. Thread A Thread B Disable sleep Sleep return enable Disable sleep Sleep return enable switch Time

Interrupt disable and enable pattern across context switches u An important point about structuring code: – If you look at the Nachos code you will see lots of comments about the assumptions made concerning when interrupts are disabled. u This is an example of where modifications to and assumptions about program state can’t be localized within a small body of code. u When that’s the case you have a very good chance that eventually your program will “acquire” bugs: as people modify the code they may forget or ignore the assumptions being made and end up invalidating the assumptions. u Can you think of other examples where this will be a concern? – What about acquiring and releasing locks in the presence of C++ exception exits out of a procedure?

Atomic read-modify-write instructions mylock.acquire(); a = b / 0; mylock.release() u Problems with this solution: – Can’t give lock implementation to users u On a multiprocessor, interrupt disable doesn’t provide atomicity. u It stops context switches from occurring on that CPU, but it doesn’t stop the other CPUs from entering the critical section. u One could provide support to disable interrupts on all CPUs, but that would be expensive: stopping everyone else, regardless of what each CPU is doing.

Atomic read-modify-write instructions u Instead, every modern processor architecture provides some kind of atomic read-modify-write instruction. u These instructions atomically read a value from memory into a register, and write a new value. u The hardware is responsible for implementing this correctly on both uniprocessors (not too hard) and multiprocessors (requires special hooks in the multiprocessor cache coherence strategy). u Unlike disabling interrupts, this can be used on both uniprocessors and multiprocessors.

Examples of read-modify-write instructions u test&set (most architectures) – read value, write 1 back to memory u exchange (x86) – swaps value between register and memory u compare&swap (68000) – read value, if value matches register, do exchange u load linked and conditional store (R4000, Alpha) – designed to fit better with load/store architecture (speculative computation). – Read value in one instruction, do some operations, when store occurs, check if value has been modified in the meantime. – If not, ok. – If it has changed, abort, and jump back to start.

Test-and-Set Instruction u The Test-and-Set instruction is executed atomically u Busy-waiting: thread consumes CPU cycles while it is waiting. Boolean TestAndSet(Boolean &target) { Boolean rv = target; target = true; return rv; } Initially: boolean lock = false; void acquire(lock) { while TestAndSet(lock) ; // while BUSY } void release(lock) { lock = false; } Thread T i : while(true) { acquire(lock) ; critical section release(lock); remainder section }

Problem: Busy-Waiting for Lock u Positives for this solution – Machine can receive interrupts – User code can use this lock – Works on a multiprocessor u Negatives – This is very inefficient because the busy-waiting thread will consume cycles waiting – Waiting thread may take cycles away from thread holding lock (no one wins!) – Priority Inversion: If busy-waiting thread has higher priority than thread holding lock  no progress! u Priority Inversion problem with original Martian rover u For semaphores and monitors, waiting thread may wait for an arbitrary length of time! – Thus even if busy-waiting was OK for locks, definitely not ok for other primitives

Test-and-Set (minimal busy waiting) u Idea: only busy-wait to atomically check lock value; if lock is busy, give up CPU. u Use a guard on the lock itself (multiple layers of critical sections!) u Waiter gives up the processor so that Release can go forward more quickly: Acquire(lock) { while test&set(guard) ; // Short busy-wait time if (value == BUSY) { Put on queue of threads waiting for lock Go to sleep & set guard to false } else { value = BUSY; guard = false; }

Test-and-Set (minimal busy waiting) u Notice that sleep has to be sure to reset the guard variable. Why can’t we do it just before or just after the sleep? Release(lock) { while (test&set(guard)) ; if anyone on wait queue { take a waiting thread off put it at the front of the ready queue } else { value = FREE; } guard = false; }

Mutual Exclusion with Swap u Swap instruction operates on the contents of two words and is executed atomically. u Atomically swap two variables. void Swap(boolean &a, boolean &b) { Boolean temp = a; a = b; b = temp; } u Shared data: Boolean lock = false; Boolean waiting[n]; u Thread T i do { key = true; while (key == true) Swap(lock,key); critical section lock = false; remainder section } while (1);

Higher-level Primitives than Locks u Goal of last couple of lectures: – What is the right abstraction for synchronizing threads that share memory? – Want as high a level primitive as possible u Good primitives and practices important! – Since execution is not entirely sequential, really hard to find bugs, since they happen rarely – UNIX is pretty stable now, but up until about mid-80s (10 years after started), systems running UNIX would crash every week or so – concurrency bugs u Synchronization is a way of coordinating multiple concurrent activities that are using share state – Next lecture presents a couple of ways of structuring the sharing

Semaphores u Synchronization primitive – higher level than locks – invented by Dijkstra in 1968, as part of the THE os – used in the original UNIX. u A semaphore is: – a non-negative integer value S, and – support two atomic operations wait/P() and signal/V() wait (S) { // also called P() while S  0 ; // no-op Spinlock S--; } signal(S) { // also called V() S++; }

Busy waiting problem u Busy waiting wastes CPU cycles. u Spinlocks are useful in multiprocessor systems. – no context switch is required when a process must wait on a lock. – Spinlocks are useful when held for short times u Each semaphore has an associated queue of processes/threads – wait(S): decrement S. If S = 0, then block until greater than zero – Signal(S): increment S by one and wake 1 waiting thread (if any) – Classic semaphores have no other operations

Hypothetical Implementation type semaphore = record value: integer: L: list of processes; end wait(S) { S.value--; if (S.value < 0) { add this process to S.L; block(); // a system call } signal(S) { S.value++; if (S.value <= 0) { remove a process P from S.L wakeup(P); // a system call } wait()/signal() are critical sections! Hence, they must be executed atomically with respect to each other. busy waiting is limited only to the critical sections of wait and signal operations, and these are short

Two Types of Semaphores u Binary semaphore: like a lock (has a Boolean value) – Initialized to 1 – A thread performs a wait() until value is 1 and then sets it to 0 – Signal() sets value to 1, waking up a waiting thread, if any u Counting semaphore: – represents a resource with many units available – allows threads/process to enter as long as more units are available – counter is initialized to N » N = number of units available

Semaphore as General Synchronization Tool u Execute B in P j only after A executed in P i u Use semaphore flag initialized to 0 u Code: P i P j   A wait(flag) signal(flag) B

Deadlock and Starvation u Deadlock – two or more processes are waiting indefinitely for an event (execution of a signal operation) that can be caused by only one of the waiting processes. u Let S and Q be two semaphores initialized to 1 P 0 P 1 wait(S);wait(Q); wait(Q);wait(S);  signal(S);signal(Q); signal(Q);signal(S); u Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended. u Indefinite blocking may occur if we add and remove processes from the list associated with a semaphore in LIFO order.

Two uses of semaphores u Mutual exclusion (initially S = 1) – Binary semaphores can be used for mutual exclusion u Process P i : do { wait(S); CriticalSection() signal(S); remainder section } while (1);

Two uses of semaphores u Scheduling constraints – Locks are fine for mutual exclusion, but what if you want a thread to wait for something? – For example, suppose you had to implement Thread::Join, which must wait for a thread to terminate. – By setting the initial value to 0 instead of 1, we can implement waiting on a semaphore: Initially S = 0 Fork Thread::Join calls wait() // will wait until something makes // the semaphore positive. Thread finish calls signal() // makes the semaphore positive // and wakes up the thread // waiting in Join.

Summary u Important concept: Atomic Operations – An operation that runs to completion or not at all – These are the primitives on which to construct various synchronization primitives u Talked about hardware atomicity primitives: – Disabling of Interrupts, test&set, swap, comp&swap, load-linked/store conditional u Showed several constructions of Locks – Must be very careful not to waste/tie up machine resources » Shouldn’t disable interrupts for long » Shouldn’t spin wait for long – Key idea: Separate lock variable, use hardware mechanisms to protect modifications of that variable u Talked about Semaphores