Chapter 5: Process Synchronization Rev. by Kyungeun Park, 2015 Operating System Concepts Essentials – 8th Edition Silberschatz, Galvin and Gagne ©2011 Chapter 5 : Process Synchronization Background The Critical-Section Problem Peterson’s Solution Synchronization Hardware Mutex Locks Semaphores Classic Problems of Synchronization Monitors Synchronization Examples Alternative Approaches Operating System Concepts Essentials – 8th Edition 6.2 Silberschatz, Galvin and Gagne ©2011 Objectives To introduce the critical-section problem, whose solutions can be used to ensure the consistency of shared data To present both software and hardware solutions of the critical-section problem To examine several critical process-synchronization problems To explore several tools that are used to solve process synchronization problems Operating System Concepts Essentials – 8th Edition 6.3 Silberschatz, Galvin and Gagne ©2011 Background Processes can execute concurrently May be interrupted at any time, partially completing execution Concurrent access of cooperating processes or threads to shared data may result in data inconsistency. Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes. Illustration of the problem: Suppose that we wanted to provide a solution to the consumer-producer problem that fills all the buffers. We can do so by having an integer count that keeps track of the number of full buffers. Initially, count is set to 0. It is incremented by the producer after it produces a new buffer and is decremented by the consumer after it consumes a buffer. Operating System Concepts Essentials – 8th Edition 6.4 Silberschatz, Galvin and Gagne ©2011 Bounded-Buffer – Shared-Memory Solution How concurrent or parallel execution affects the integrity of data shared by several processes? Shared buffer: a circular array with two logical pointers: in and out #define BUFFER_SIZE 10 typedef struct { ... } item; item buffer[BUFFER_SIZE]; int in = 0; /* the next free position in the buffer */ int out = 0; /* the first full position in the buffer */ in Initial Empty Buffer (size 10) 0 1 2 9 out Operating System Concepts Essentials – 8th Edition 6.5 Silberschatz, Galvin and Gagne ©2011 Producer while (true) { /* Produce an item and put in nextProduced */ while (counter == BUFFER_SIZE) ; // do nothing buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; counter++; } * counter++ could be implemented in machine language as register1 = counter register1 = register1 + 1 counter = register1 Operating System Concepts Essentials – 8th Edition 6.6 Silberschatz, Galvin and Gagne ©2011 Consumer while (true) { while (counter == 0) ; // do nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; counter--; /* Consume the item in nextConsumed */ } * counter-- could be implemented in machine language as register2 = counter register2 = register2 - 1 count = register2 Operating System Concepts Essentials – 8th Edition 6.7 Silberschatz, Galvin and Gagne ©2011 Race Condition Both the producer and consumer routines may not function correctly when executed concurrently. Consider this execution interleaving with “count = 5” initially: S0: producer execute register1 = counter {register1 = 5} S1: producer execute register1 = register1 + 1 {register1 = 6} S2: consumer execute register2 = counter {register2 = 5} S3: consumer execute register2 = register2 - 1 {register2 = 4} S4: producer execute counter = register1 {counter = 6 } S5: consumer execute counter = register2 {counter = 4} Race Condition : a situation where several processes access and manipulate the same data concurrently and the outcome of the execution depends on the particular order in which the access takes place. Ensure that only one process at a time can manipulate the variable counter. Process synchronization and coordination among cooperating processes Operating System Concepts Essentials – 8th Edition 6.8 Silberschatz, Galvin and Gagne ©2011 Critical Section Problem Consider system of n processes {p0, p1, … pn-1} Each process has a segment of code, critical section, where Process may be changing common variables, updating a table, writing a file, etc. When one process is executing in critical section, no other process is allowed to execute in its critical section. Critical section problem is to design a protocol that the processes can use to cooperate. Each process must ask permission to enter its critical section in the entry section. Followed by an exit section, then remainder section. Kernel code cause race conditions Ex) a list of all open files maintained by an operating system. data structures for maintaining memory allocation and process lists or for handling interrupts Operating System Concepts Essentials – 8th Edition 6.9 Silberschatz, Galvin and Gagne ©2011 Critical Section General structure of process pi is do { entry section critical section exit section remainder section } while (TRUE) Operating System Concepts Essentials – 8th Edition 6.10 Silberschatz, Galvin and Gagne ©2011 Solution to Critical-Section Problem (1) Three requirements: 1. Mutual Exclusion - If process Pi is executing in its critical section, then no other processes can be executing in their critical sections. 2. Progress - If no process is executing in its critical section and there exist some processes that wish to enter their critical section, then only those processes that are not executing in their remainder sections can participate in deciding which will enter its critical section next, and this selection cannot be postponed indefinitely. 3. Bounded Waiting - There exists a bound, or limit on the number of times that other processes are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted. Note Assume that each process executes at a nonzero speed No assumption concerning relative speed of the n processes Operating System Concepts Essentials – 8th Edition 6.11 Silberschatz, Galvin and Gagne ©2011 Solution to Critical-Section Problem (2) Two general approaches used to handle critical sections in operating systems Preemptive kernel – allows preemption of process when running in kernel mode Ex) two kernel-mode processes running simultaneously on different processors in SMP environment Non-preemptive kernel – a process runs until it exits kernel mode, blocks, or voluntarily yields CPU Essentially free from race conditions on kernel data structures Then why preemptive kernel over non-preemptive one? preemptive kernel : complicated, but more responsive and more suitable for real-time programming allowing a real-time process to preempt a process currently running in the kernel Operating System Concepts Essentials – 8th Edition 6.12 Silberschatz, Galvin and Gagne ©2011 Peterson’s Solution Peterson’s solution: a classic software-based solution to the critical-section problem Worth reviewing the underlying algorithm to solve the critical section problem and the way of reasoning how to satisfy three requirements Restricted to two processes, P0 and P1, that alternate execution between their critical sections and remainder sections Operating System Concepts Essentials – 8th Edition 6.13 Silberschatz, Galvin and Gagne ©2011 Algorithm for Process Pi do { flag[i] = true; turn = j; while (flag[j] && turn == j); critical section flag[i] = false; remainder section } while (TRUE); do { flag[j] = true; turn = i; while (flag[i] && turn == i); critical section flag[j] = false; remainder section } while (TRUE); * at Process Pi The two processes share two data items: int turn; boolean flag[2] The flag array is used to indicate if a process is ready to enter the critical section. * at Process Pj flag[i] = true implies that process Pi is ready! The variable turn indicates whose turn it is to enter the critical section if both processes try to enter at the same time, turn will be set to both i and j at roughly the same time only one of these assignments will last the value of turn determines which of the two processes is allowed to enter its critical section first Operating System Concepts Essentials – 8th Edition 6.14 Silberschatz, Galvin and Gagne ©2011 Algorithm for Process Pi do { do { flag[i] = true; turn = j; while (flag[j] && turn == j); critical section flag[i] = false; remainder section } while (TRUE); turn would be 0 or 1 * at Process Pi flag[j] = true; turn = i; while (flag[i] && turn == i); critical section flag[j] = false; remainder section } while (TRUE); * at Process Pj Prove: assume both processes executes in their critical sections at the same time 1. Mutual exclusion is preserved: When flag[j]=false or turn==i, each Pi enters its critical section. To exit while loop and enter their critical sections, as flag[i]=true and flag[j]=true (insufficient) So the value of turn should be 0 for P0 and 1 for P1 at the same time : impossible! 2. Progress requirement is satisfied: Bounded-waiting requirement is met: Pi can be prevented from entering the CS only if it is stuck in the while loop If Pj is not ready to enter the critical section, then flag[j]=false and Pi can enter its CS If Pj has set flag[j] to true and is also executing in its while loop, then either turn==i or turn==j. If turn==i then Pi will enter the CS: If turn==j then Pj will enter the CS. If Pj exits its CS, it will reset flag[j] to false, allowing Pi to enter its CS. If Pj resets flag[j] to true immediately, it must also set turn to i. Since no change on turn in Pi 3. Pi will enter its CS (Progress) after at most one entry by Pj (bounded waiting). Operating System Concepts Essentials – 8th Edition 6.15 Silberschatz, Galvin and Gagne ©2011 Synchronization Hardware Many systems provide hardware support for critical section code All solutions (from hardware to software-based APIs) based on idea of locking Uniprocessors – could disable interrupts while modifying a shared variable Protecting critical regions through the use of locks Currently running code would execute without preemption No other instructions would be run, so no unexpected modifications to the shared variables Generally too inefficient on multiprocessor environments Disabling interrupt for all processors by sending the message: causes time consuming and delays Modern machines provide special atomic hardware instructions Atomic : non-interruptable, uninterruptible Allow us either test memory word and set the value : TestAndSet( ) instruction Or swap contents of two memory words : CompareAndSwap( ) instruction Operating System Concepts Essentials – 8th Edition 6.16 Silberschatz, Galvin and Gagne ©2011 TestAndSet() Instruction Definition: run atomically as an uninterruptible instruction if two instructions are executed simultaneously on a different CPU, they will be executed sequentially in some order boolean TestAndSet (boolean *target) { boolean rv = *target; *target = TRUE; return rv: } Operating System Concepts Essentials – 8th Edition 6.17 Silberschatz, Galvin and Gagne ©2011 Solution using TestAndSet() Shared boolean variable lock, initialized to FALSE Solution: do { while (TestAndSet (&lock )) /* run as an uninterruptible unit */ ; // do nothing // critical section lock = FALSE; // remainder section } while (TRUE); Operating System Concepts Essentials – 8th Edition 6.18 Silberschatz, Galvin and Gagne ©2011 CompareAndSwap() Instruction Definition: operates on three operands. int CompareAndSwap (int *value, int expected, int new_value) { int temp = *value; if (*value == expected) *value = new value; return temp; } Operating System Concepts Essentials – 8th Edition 6.19 Silberschatz, Galvin and Gagne ©2011 Solution using CompareAndSwap() Global variable, lock initialized to 0 The first process will set lock to 1 and enter its CS because lock == 0 inside CompareAndSwap() instruction. When the process exits the CS, it sets lock back to 0, allowing another process to enter its CS. Solution: do { while (CompareAndSwap(&lock, 0, 1) != 0) ; /* do nothing */ // critical section lock = 0; // remainder section } while (TRUE); ME is met; but bounded waiting is not satisfied Operating System Concepts Essentials – 8th Edition 6.20 Silberschatz, Galvin and Gagne ©2011 Bounded-waiting Mutual Exclusion with TestAndSet() do { // lock : false, waiting[n] : all false waiting[i] = true; key = true; while (waiting[i] && key) The two processes share two data items: key = TestAndSet(&lock); waiting[i] = false; Mutual Exclusion: Pi can enter its CS only if either waiting[i] == false or key == false // critical section j = (i + 1) % n; while ((j != i) && !waiting[j]) j = (j + 1) % n; Progress: since a process exiting the CS either sets lock to false or sets waiting[j] to false, allowing a process waiting to enter its CS to proceed Bounded Waiting Time: a leaving process scans the array waiting in the cyclic ordering. Any waiting process will enter CS within n-1 times. lock = false; // initial state else // within n-1 times // remainder section } while (true); Operating System Concepts Essentials – 8th Edition key = false only if TestAndSet() executed ex) the first process of TestAndSet() waiting[i] = false if another process leaves its CS if (j == i) // one cycle waiting[j] = false; // take turns boolean waiting[n]; // initialized to False boolean lock; // initialized to False 6.21 Silberschatz, Galvin and Gagne ©2011 Bounded-waiting Mutual Exclusion with TestandSet() do { do { waiting[i] = TRUE; waiting[j] = TRUE; key = TRUE; key = TRUE; while (waiting[i] && key) while (waiting[j] && key) key = TestAndSet(&lock); key = TestAndSet(&lock); waiting[i] = FALSE; waiting[j] = FALSE; // critical section // critical section j = (i + 1) % n; i = (j + 1) % n; while ((j != i) && !waiting[j]) while ((i != j) && !waiting[i]) j = (j + 1) % n; i = (i + 1) % n; if (j == i) if (i == j) lock = FALSE; lock = FALSE; else else waiting[j] = FALSE; waiting[i] = FALSE; // remainder section // remainder section } while (TRUE); } while (TRUE); * at Process Pi Operating System Concepts Essentials – 8th Edition 6.22 * at Process Pj Silberschatz, Galvin and Gagne ©2011 Mutex Locks Previous solutions are complicated and generally inaccessible to application programmers OS designers build software tools to solve critical section problem Simplest is mutex lock A process should acquire a lock before entering a critical section; It releases the lock when it exits the critical section. Protect critical regions and prevent race conditions: By first acquire() a lock, then release() it do { Boolean variable indicating if lock is available or not Calls to acquire() and release() must be atomic acquire lock Usually implemented via hardware atomic instructions: critical section using TestAndSet() or CompareAndSwap() instructions release lock Disadv) But this solution requires busy waiting remainder section This lock therefore called a spinlock } while (true); good for short term lock (No context-switch) Busy waiting wastes CPU cycles that some other processes uses productively. Operating System Concepts Essentials – 8th Edition 6.23 Silberschatz, Galvin and Gagne ©2011 acquire() and release() acquire() { while (!available) ; /* busy wait */ available = false;; } release() { available = true; } Solution to the critical section problem using mutex lock do { acquire lock critical section release lock remainder section } while (true); Operating System Concepts Essentials – 8th Edition 6.24 Silberschatz, Galvin and Gagne ©2011 Semaphore Previous hardware-based solutions: hard for application programmers to use. Synchronization tool, similar to a mutex lock, a semaphore is introduced. Semaphore S – integer variable Generally a counter used to provide access to a shared data object for multiple processes. Accessed only through two standard atomic operations: wait() and signal() wait() originally termed P, proberen, “to test” signal() originally called V, verhogen, “to increment” Less complicated Can only be accessed via two indivisible (atomic) operations wait (S) { while S <= 0; // no-op, busy wait S--; } signal (S) { S++; } Operating System Concepts Essentials – 8th Edition 6.25 Silberschatz, Galvin and Gagne ©2011 Semaphore as General Synchronization Tool Counting semaphore – integer value can range over an unrestricted domain Used to control access to a given resource consisting of a finite number of instances. The semaphore is initialized to the number of resources available. wait(S) when wishing to use a resource , signal(S) when releasing a resource Binary semaphore – integer value can range only between 0 and 1; Also known as mutex locks, initialized to 1 Can implement a counting semaphore S as a binary semaphore Provides mutual exclusion Semaphore mutex; // initialized to 1 do { wait (mutex); // Critical Section signal (mutex); // remainder section } while (TRUE); Operating System Concepts Essentials – 8th Edition 6.26 Silberschatz, Galvin and Gagne ©2011 Semaphore as General Synchronization Tool Synchronization problems Two concurrently running processes: P1 and P2 P1 with a statement S1 P2 with a statement S2 S2 to be executed only after S1 has completed Common semaphore synch, initialized to 0, for P1 and P2 S1; signal(synch); // in process P1 wait(synch); // in process P2 S2; Note: because synch is initialized to 0, P2 will execute S2 only after P1 has invoked signal(synch) Operating System Concepts Essentials – 8th Edition 6.27 Silberschatz, Galvin and Gagne ©2011 Characteristics of Semaphore Resource sharing by a process: Test the semaphore that controls the resource Positive semaphore value use the resource, decrement by 1 0 semaphore value go to sleep until (semaphore > 0) Done with a shared resource increment by 1, awakening sleeping processes Main disadvantage of the semaphore definition is that it requires busy waiting. While a process is in its critical section, any other process that tries to enter its critical section must loop continuously in the entry code. Continual looping a problem in a multiprogramming system with a single CPU Busy waiting wastes CPU cycle that some other process might use. This type of semaphore is called a spinlock (spinning while waiting for the lock) Useful for multiprocessor systems as the alternative to the context switch when locks are expected to be held for short time. Operating System Concepts Essentials – 8th Edition 6.28 Silberschatz, Galvin and Gagne ©2011 Semaphore Implementation with no Busy waiting To overcome the need for busy waiting: With each semaphore there is an associated waiting queue, Modification of the definition of the wait() and signal() semaphore operations a process executes wait() and finds the semaphore is not positive wait and rather than busy waiting, block itself placing it into a waiting queue associated with the semaphore process state to waiting state control to the CPU scheduler resulting in selecting another process the process waiting on a semaphore should be restarted when some other process executes a signal() Two operations provided by the OS as basic system calls block() – places the process invoking the operation on the appropriate waiting queue, suspending the process that invokes it. wakeup() – remove one of processes in the waiting queue and place it in the ready queue to restart it, changing from the waiting state to the ready state. Operating System Concepts Essentials – 8th Edition 6.29 Silberschatz, Galvin and Gagne ©2011 Semaphore Implementation with no Busy waiting Each entry (semaphore) in a waiting queue has two data items: value (of type integer) list : pointer to a list of processes typedef struct { int value; struct process * list; } semaphore; Operating System Concepts Essentials – 8th Edition 6.30 Silberschatz, Galvin and Gagne ©2011 Revised Semaphore Implementation Implementation of wait() : wait(semaphore *S) { Svalue--; // semaphore value may be negative, identifying # of if (Svalue < 0) { // processes waiting on that semaphore add this process to Slist; block(); // suspends the process that invokes it } } Implementation of signal() : signal(semaphore *S) { Svalue++; if (Svalue <= 0) { remove a process P from Slist; wakeup(P); // resumes the execution of a blocked process P } } Operating System Concepts Essentials – 8th Edition 6.31 Silberschatz, Galvin and Gagne ©2011 Semaphore Implementation Must guarantee that no two processes can execute wait () and signal () on the same semaphore at the same time Thus, implementation becomes the critical section problem where the wait and signal code are placed in the critical section In a single-processor environment, solved by disabling interrupts during the time the wait() and signal() operations are executing. In a multiprocessor environment, interrupts must be disabled on every processor. This is a difficult task and causes performance degradation. SMP system must provide a locking techniques, such as spinlocks. Still, have busy waiting with wait() and signal() operations in critical section implementation Problem moved from the entry section to the critical sections of wait() and signal() operations But they are usually short critical section rarely occupied Note that applications may contain long CSs and spend lots of time Busy waiting is not a good solution. Operating System Concepts Essentials – 8th Edition 6.32 Silberschatz, Galvin and Gagne ©2011 Deadlock and Starvation Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes. Definition (from Wiki ) A deadlock is the situation where a group of processes blocks forever because each of the processes is waiting for resources which are held by another process in the group. Let S and Q be two semaphores initialized to 1 P0 P1 wait (S); wait (Q); wait (Q); wait (S); … … signal (S); signal (Q); Starvation – indefinite blocking (LIFO queue) signal (Q); signal (S); A process may never be removed from the semaphore queue in which it is suspended Priority Inversion – Scheduling problem when lower-priority process holds a lock needed by higher-priority process Solved via priority-inheritance protocol (by inheriting the higher priority until the lower priority process finishes using resource R, needed by the higher-priority process. Operating System Concepts Essentials – 8th Edition 6.33 Silberschatz, Galvin and Gagne ©2011 Classical Problems of Synchronization Classical problems used to test newly-proposed synchronization schemes, semaphores (mutex locks) Bounded-Buffer Problem Readers and Writers Problem Dining-Philosophers Problem Operating System Concepts Essentials – 8th Edition 6.34 Silberschatz, Galvin and Gagne ©2011 Bounded-Buffer Problem N buffers, each can hold one item Semaphore mutex, initialized to the value 1, providing mutual exclusion for accesses to the buffer pool Semaphore full, initialized to the value 0, count the # of full buffers Semaphore empty, initialized to the value N, count the # of empty buffers Operating System Concepts Essentials – 8th Edition 6.35 Silberschatz, Galvin and Gagne ©2011 Bounded Buffer Problem with Semaphore The structure of the producer process do { The structure of the consumer process do { wait (full); // produce an item wait (mutex); // in next_produced // remove an item wait (empty); // from buffer to next_consumed wait (mutex); signal (mutex); // add the item to the buffer signal (empty); signal (mutex); // consume the item signal (full); // in next_consumed } while (TRUE); Operating System Concepts Essentials – 8th Edition } while (TRUE); 6.36 Silberschatz, Galvin and Gagne ©2011 Readers-Writers Problem A database is shared among several concurrent processes Readers – only read the database; they do not perform any updates Writers – can update (both read and write) the database Database access by reader(s) and writer(s), simultaneously causes chaos. Require that the writers have exclusive access to the shared database while writing to the database Synchronization problem: Readers-writers problem Problem – allow multiple readers to read at the same time Only one single writer can access the shared data exclusively Shared Data Semaphore mutex, initialized to 1, ensures mutual exclusion when read_count is updated Semaphore rw_mutex, initialized to 1, common to both reader and writer processes, functions as a mutual exclusion semaphore for the writers, and used by the first and last reader, and not used by readers while other readers are in their CS. Integer read_count, initialized to 0, keeps track of how many processes are currently reading the object. Operating System Concepts Essentials – 8th Edition 6.37 Silberschatz, Galvin and Gagne ©2011 Readers-Writers Problem (Cont.) The structure of a writer process The structure of a reader process do { do { wait (mutex) ; // down to access read_count read_count++ ; if (read_count == 1) // if first process to read wait (rw_mutex) ; // wait until no write signal (mutex) // up for releasing read_count wait (rw_mutex) ; // writing is performed signal (rw_mutex) ; // reading is performed } while (TRUE); wait (mutex) ; // down mutex read_count-- ; if (read_count == 0) // if no more reader signal (rw_mutex) ; // allow a writer signal (mutex) ; // up mutex } while (TRUE); Operating System Concepts Essentials – 8th Edition 6.38 Silberschatz, Galvin and Gagne ©2011 Readers-Writers Problem with reader-writer lock Problem is solved on some systems by kernel providing reader-writer locks Specifying the mode of the lock: either read or write access Only for reading? acquire the reader-writer lock in read mode Wishing to modify? request the lock in write mode Useful when it is easy to identify which one is reader or writer for shared data Useful for applications that have more readers than writers : increased concurrency compensates the overhead to implement reader-writer lock Operating System Concepts Essentials – 8th Edition 6.39 Silberschatz, Galvin and Gagne ©2011 Dining-Philosophers Problem Each philosophers must alternately think and eat. Originally formulated in 1965 by E. Dijkstra as a competing problem for access to tape drive peripherals (Wiki) Don’t interact with their neighbors, occasionally try to pick up 2 (left and right) chopsticks (one at a time) to eat from bowl Need both to eat, then release both when done In the case of 5 philosophers Shared data Bowl of rice (data set): Infinite supply Semaphore chopstick [5], initialized to 1 Operating System Concepts Essentials – 8th Edition 6.40 Silberschatz, Galvin and Gagne ©2011 Dining-Philosophers Problem Algorithm The structure of Philosopher i: do { wait ( chopstick[i] ); wait ( chopstick[ (i + 1) % 5] ); // wait for left chopstick // wait for right chopstick // eat for a while signal ( chopstick[i] ); signal ( chopstick[ (i + 1) % 5] ); // think for a while } while (TRUE); What is the problem with this algorithm? Deadlock: simultaneously grab left chopstick and delayed forever Starvation? Operating System Concepts Essentials – 8th Edition 6.41 Silberschatz, Galvin and Gagne ©2011 Problems with Semaphores Incorrect use of semaphore operations causes various types of errors Typical CS solution with semaphore (mutex, initialized to 1): wait (mutex) …. signal (mutex) what if signal (mutex) …. wait (mutex) ? ME violation because several processes in CS, but found only if it happens wait (mutex) … wait (mutex) Deadlock Omitting of wait (mutex) or signal (mutex) (or both) ME violation or Deadlock As a solution to this incorrect use of semaphores, high-level language constructs, the monitor type has been developed. Operating System Concepts Essentials – 8th Edition 6.42 Silberschatz, Galvin and Gagne ©2011 Monitors A high-level abstraction that provides a convenient and effective mechanism for process synchronization A monitor type is an abstract data type, a collection of shared variables and operations on those variables: only accessible by code within the procedure Only one process (thread) may be active within the monitor at a time Implicit mutual exclusion, No need to code the synchronization constraint explicitly. monitor monitor-name { // shared variable declarations procedure P1 (…) { …. } procedure Pn (…) {……} Initialization code (…) { … } } But not powerful enough to model some synchronization schemes Additional explicit synchronization mechanisms to write a tailor-made synchronization scheme Provided by the condition construct Operating System Concepts Essentials – 8th Edition 6.43 Silberschatz, Galvin and Gagne ©2011 Schematic view of a Monitor Operating System Concepts Essentials – 8th Edition 6.44 Silberschatz, Galvin and Gagne ©2011 Condition Variables Monitor condition variables condition x, y; // condition type variables Only two operations on a condition variable: x.wait () – a process invoking the operation is suspended until x.signal () is invoked Put executing process (thread) at the end of the condition variable queue Releases access to the monitor x.signal () – resumes exactly one suspended process (if any) that invoked x.wait () If no x.wait () on the variable, then it has no effect on the variable c.f. signal() associated with semaphores : always affect the state of semaphore Operating System Concepts Essentials – 8th Edition 6.45 Silberschatz, Galvin and Gagne ©2011 Monitor with Condition Variables Operating System Concepts Essentials – 8th Edition 6.46 Silberschatz, Galvin and Gagne ©2011 Monitor Behavior (1) Ref. for Monitor with semaphore: http://www.eecs.harvard.edu/~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf Operating System Concepts Essentials – 8th Edition 6.47 Silberschatz, Galvin and Gagne ©2011 Monitor Behavior (2) Ref. for Monitor with semaphore: http://www.eecs.harvard.edu/~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf Operating System Concepts Essentials – 8th Edition 6.48 Silberschatz, Galvin and Gagne ©2011 Monitor Behavior (3) Ref. for Monitor with semaphore: http://www.eecs.harvard.edu/~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf Operating System Concepts Essentials – 8th Edition 6.49 Silberschatz, Galvin and Gagne ©2011 Condition Variables Choices If process P invokes x.signal (), with Q in x.wait () state, what should happen next? If Q is resumed, then P must wait within the monitor. Otherwise, both P and Q processes would be active simultaneously within the monitor. Two possibilities exist: Signal and wait – P waits until Q leaves monitor or waits for another condition Signal and continue – Q waits until P leaves the monitor or waits for another condition Both have pros and cons – language implementer can decide Monitors implemented in Concurrent Pascal compromise P executing signal() immediately leaves the monitor. Hence Q is resumed. (signal_and_leave) Implemented in other languages including Java and C#. Operating System Concepts Essentials – 8th Edition 6.50 Silberschatz, Galvin and Gagne ©2011 Dining-Philosophers Solution Using Monitors Deadlock-free solution to the dining-philosophers problem Restriction: A philosopher may pick up her chopsticks only if both of them are available. Additional data structure to distinguish the philosophers’ state enum {THINKING, HUNGRY, EATING} state[5]; //Three states of a philosopher condition self[5]; Each philosopher i invokes the operations pickup() and putdown() in the following sequence: DiningPhilosophers.pickup(i); EAT DiningPhilosophers.putdown(i); No deadlock, but starvation is possible! Operating System Concepts Essentials – 8th Edition 6.51 Silberschatz, Galvin and Gagne ©2011 Solution to DP using Monitor(Cont.) monitor DiningPhilosophers { enum { THINKING, HUNGRY, EATING } state [5] ; condition self [5]; void pickup (int i) void putdown (int i) void test (int i) void initialization_code() } Operating System Concepts Essentials – 8th Edition 6.52 Silberschatz, Galvin and Gagne ©2011 Solution to DP using Monitor(Cont.) monitor DiningPhilosophers { enum { THINKING, HUNGRY, EATING } state [5] ; condition self [5]; void test (int i) { if ( (state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING; self[i].signal(); } } void pickup (int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self [i].wait(); } void putdown (int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); } void initialization_code() { for (int i = 0; i < 5; i++) state[i] = THINKING; } } Operating System Concepts Essentials – 8th Edition 6.53 Silberschatz, Galvin and Gagne ©2011 Monitor Implementation Using Semaphores Variables semaphore mutex; // (initially = 1), provided for each monitor semaphore next; // (initially = 0), used by the signaling process // to suspend themselves and wait until the // resumed process either leaves or waits // (signal_and_wait) int next_count = 0; // provided to count the # of processes suspended on next Each external function F will be replaced by: wait(mutex); // before entering the monitor … body of F; … if (next_count > 0) // if there is process suspended on next signal(next) else signal(mutex); // after leaving the monitor Mutual exclusion within a monitor is ensured. Operating System Concepts Essentials – 8th Edition 6.54 Silberschatz, Galvin and Gagne ©2011 Condition Variables Implementation in Monitor For each condition variable x, we have: semaphore x_sem; // (initially = 0) int x_count = 0; The operation x.wait() can be implemented as: x_count++; if (next_count > 0) signal(next); else signal(mutex); wait(x_sem); x_count--; The operation x.signal() can be implemented as: // unlock the monitor for // processes waiting outside // the monitor // wait on the CV queue // can be taken out of CV q. Operating System Concepts Essentials – 8th Edition 6.55 if (x_count > 0) { // only if processes on CV next_count++; signal(x_sem); wait(next); // signal and wait // causes signaling process // to suspend themselves //(by Hoare style) next_count--; } Silberschatz, Galvin and Gagne ©2011 Monitor Behavior (4) Ref. for Monitor with semaphore: http://www.eecs.harvard.edu/~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf Operating System Concepts Essentials – 8th Edition 6.56 Silberschatz, Galvin and Gagne ©2011 Monitor Behavior (5) Ref. for Monitor with semaphore: http://www.eecs.harvard.edu/~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf Operating System Concepts Essentials – 8th Edition 6.57 Silberschatz, Galvin and Gagne ©2011 Signaling on CV block After signaling on CV, immediately leave (or execute) Ref. for Monitor with semaphore: http://www.eecs.harvard.edu/~mdw/course/cs61/mediawiki/images/7/7e/Lectures-semaphores.pdf Operating System Concepts Essentials – 8th Edition 6.58 Silberschatz, Galvin and Gagne ©2011 Resuming Processes within a Monitor If several processes queued on condition x, and x.signal() executed, which should be resumed? Process-resumption order: First-come, first-served (FCFS) : frequently, but not adequate in some cases conditional-wait construct of the form: x.wait(c) where c is priority number (an integer expression): used as the priority number of a process stored with the name of the process Process with lowest number (highest priority) is scheduled next Operating System Concepts Essentials – 8th Edition 6.59 Silberschatz, Galvin and Gagne ©2011 A Monitor to Allocate Single Resource ResourceAllocator monitor controls the allocation of a single resource among competing processes. Requesting process specifies the maximum time it plans to use the resource. monitor ResourceAllocator { boolean busy; condition x; void acquire(int time) { if (busy) x.wait(time); // monitor allocates the resource to the process busy = TRUE; // with the shortest time-allocation request } void release() { busy = FALSE; x.signal(); } initialization code() { busy = FALSE; } } Operating System Concepts Essentials – 8th Edition 6.60 Silberschatz, Galvin and Gagne ©2011 Synchronization Examples Windows Linux Solaris Pthreads Operating System Concepts Essentials – 8th Edition 6.61 Silberschatz, Galvin and Gagne ©2011 Windows Synchronization Windows OS is a multithreaded kernel for real-time applications and multiple processors When kernel accesses a global resource: a single processor system: temporarily masks interrupts for all interrupt handlers to protect access to global resources a multiprocessor system: protects access to global resources using spinlocks (short code segments), non-preempting threads while holding a spinlock Spinlocking-thread will never be preempted Also provides dispatcher objects for thread synchronization outside the kernel, which may implement mutex locks, semaphores, events, and timers. Events An event acts much like a condition variable Timers notify one or more thread when time expired Dispatcher objects in either signaled-state (object available) or non-signaled state (object not available, thread will block) Operating System Concepts Essentials – 8th Edition 6.62 Silberschatz, Galvin and Gagne ©2011 Linux Synchronization Linux: Prior to kernel Version 2.6, disables interrupts (non-preemptive) to implement short critical sections Now (Version 2.6 and later), fully preemptive Atomic simple math operations for the simplest synchronization technique: atomic_t, atomic_set(), atomic_add(), atomic_sub(), atomic_inc(), atomic_read() Linux provides synchronization mechanisms in the kernel: mutex locks semaphores spinlocks reader-writer versions of both For SMP, spinlock is provided On single-processor system, spinlocks are inappropriate for use and are replaced by Enabling kernel preemption, rather than releasing a spinlock (SMP) preempt_enable() Disabling kernel preemption, rather than holding a spinlock (SMP) preempt_disable() Operating System Concepts Essentials – 8th Edition 6.63 Silberschatz, Galvin and Gagne ©2011 Solaris Synchronization Implements a variety of locks to support multitasking, multithreading (including realtime threads), and multiprocessing Uses adaptive mutex for efficiency when protecting critical data item from short code segments On SMP: adaptive mutex starts as a standard semaphore implemented as a spinlock If the data are locked and in use, two choices: either spin or block If held by a thread running on another CPU, then the thread spins If lock held by non-run-state thread, the thread blocks and sleeps waiting for signal of lock being released Uses condition variables and semaphores for longer code segment Uses readers-writers locks when protecting data accessed frequently but in a read-only manner (multiple readers concurrently) and useful for long sections of code Uses turnstiles, a queue structure containing threads blocked on a lock, to order the list of threads waiting to acquire either an adaptive mutex or reader-writer lock Turnstiles are per-lock-holding-thread, not per-object Priority-inheritance per-turnstile gives the running thread the highest of the priorities of the threads in its turnstile Operating System Concepts Essentials – 8th Edition 6.64 Silberschatz, Galvin and Gagne ©2011 Pthreads Synchronization Pthreads API is OS-independent It provides: mutex locks : fundamental synchronization technique used with Pthreads condition variables Non-portable extensions include: semaphores spinlocks Operating System Concepts Essentials – 8th Edition 6.65 Silberschatz, Galvin and Gagne ©2011 The Deadlock Problem In a multiprogramming environment, a set of blocked processes each holding a resource and waiting to acquire a resource held by another process in the set Example System has 2 disk drives P1 and P2 each hold one disk drive and each needs another one Example semaphores A and B, initialized to 1 P0 and P1 wait (A); wait(B) wait (B); wait(A) Operating System Concepts Essentials – 8th Edition 6.66 Silberschatz, Galvin and Gagne ©2011 Bridge Crossing Example Traffic only in one direction Each section of a bridge can be viewed as a resource If a deadlock occurs, it can be resolved if one car backs up (preempt resources and rollback) Several cars may have to be backed up if a deadlock occurs Starvation is possible Note – Most OSes do not prevent or deal with deadlocks Operating System Concepts Essentials – 8th Edition 6.67 Silberschatz, Galvin and Gagne ©2011 System Model A system consists of a finite number of resources to be distributed among a number of competing processes. Resource types R1, R2, . . ., Rm CPU cycles, memory space, files, I/O devices Each resource type Ri has Wi instances. Each process utilizes a resource in only the following sequence request use release Operating System Concepts Essentials – 8th Edition 6.68 Silberschatz, Galvin and Gagne ©2011 Deadlock Characterization Deadlock can arise if the following four conditions hold simultaneously. Mutual exclusion: only one process at a time can use a resource in a nonsharable mode No preemption: a resource can be released only voluntarily by the process holding it, after that process has completed its task Hold and wait: a process holding at least one resource is waiting to acquire additional resources held by other processes Circular wait: there exists a set {P0, P1, …, Pn} of waiting processes such that P0 is waiting for a resource that is held by P1, P1 is waiting for a resource that is held by P2, …, Pn–1 is waiting for a resource that is held by Pn, and Pn is waiting for a resource that is held by P0. Operating System Concepts Essentials – 8th Edition 6.69 Silberschatz, Galvin and Gagne ©2011 Simple Resource Deadlock Operating System Concepts Essentials – 8th Edition 6.70 Silberschatz, Galvin and Gagne ©2011 Related: Indefinite postponement Also called indefinite blocking or starvation Occurs due to biases in a system’s resource scheduling policies Aging Technique that prevents indefinite postponement by increasing process’s priority as it waits for resource COP 5994 - Operating Systems Operating System Concepts Essentials – 8th Edition 6.71 71 Silberschatz, Galvin and Gagne ©2011 Resource-Allocation Graph A set of vertices V and a set of edges E V is partitioned into two types: P = {P1, P2, …, Pn}, the set consisting of all the processes in the system R = {R1, R2, …, Rm}, the set consisting of all resource types in the system E is directed edge from process to resource type request edge – directed edge Pi Rj assignment edge – directed edge Rj Pi Operating System Concepts Essentials – 8th Edition 6.72 Silberschatz, Galvin and Gagne ©2011 Resource-Allocation Graph (Cont.) Process Resource Type with 4 instances Pi requests instance of Rj request edge – directed edge Pi Rj Pi Rj Pi is holding an instance of Rj assignment edge – directed edge Rj Pi Pi Rj Operating System Concepts Essentials – 8th Edition 6.73 Silberschatz, Galvin and Gagne ©2011 Example of a Resource Allocation Graph Operating System Concepts Essentials – 8th Edition 6.74 Silberschatz, Galvin and Gagne ©2011 Resource Allocation Graph With A Deadlock Operating System Concepts Essentials – 8th Edition 6.75 Silberschatz, Galvin and Gagne ©2011 Graph With A Cycle But No Deadlock Operating System Concepts Essentials – 8th Edition 6.76 Silberschatz, Galvin and Gagne ©2011 Basic Facts If graph contains no cycles no deadlock If graph contains a cycle if only one instance per resource type, then deadlock if several instances per resource type, possibility of deadlock Operating System Concepts Essentials – 8th Edition 6.77 Silberschatz, Galvin and Gagne ©2011 Methods for Handling Deadlocks Prevent or avoid deadlocks by ensuring that the system will never enter a deadlock state Allow the system to enter a deadlock state and then recover Ignore the problem and pretend that deadlocks never occur in the system; used by most operating systems, including UNIX Operating System Concepts Essentials – 8th Edition 6.78 Silberschatz, Galvin and Gagne ©2011 Dealing with Deadlock Deadlock prevention Deadlock avoidance Deadlock detection Deadlock recovery Operating System Concepts Essentials – 8th Edition 6.79 Silberschatz, Galvin and Gagne ©2011 Deadlock Prevention Includes a set of methods for ensuring that at least one of the necessary conditions cannot hold. Prevent deadlocks by constraining request operations To prevent Mutual Exclusion – not required for sharable resources; must hold for nonsharable resources To prevent Hold and Wait – must guarantee that whenever a process requests a resource, it does not hold any other resources Require process to request and be allocated all its resources before it begins execution, or allow process to request resources only when the process has none Low resource utilization; starvation possible Operating System Concepts Essentials – 8th Edition 6.80 Silberschatz, Galvin and Gagne ©2011 Deadlock Prevention (Cont.) To prevent No Preemption – If a process that is holding some resources requests another resource that cannot be immediately allocated to it, then all resources currently being held are released Preempted resources are added to the list of resources for which the process is waiting Process will be restarted only when it can regain its old resources, as well as the new ones that it is requesting To prevent Circular Wait – impose a total ordering of all resource types, and require that each process requests resources in an increasing order of enumeration Operating System Concepts Essentials – 8th Edition 6.81 Silberschatz, Galvin and Gagne ©2011 Deadlock Avoidance Requires that the system has some additional information concerning which resources a process will request and use during its lifetime Simplest and most useful model requires that each process declare the maximum number of resources of each type that it may need The deadlock-avoidance algorithm dynamically examines the resourceallocation state to ensure that there can never be a circular-wait condition Resource-allocation state is defined by the number of available and allocated resources, and the maximum demands of the processes Operating System Concepts Essentials – 8th Edition 6.82 Silberschatz, Galvin and Gagne ©2011