Download presentation
Presentation is loading. Please wait.
Published byCandice Sanders Modified over 9 years ago
1
Synchronization CSCI 3753 Operating Systems Spring 2005 Prof. Rick Han
2
Announcements HW #3 is coming, due Friday Feb. 25, a week+ from now PA #2 is coming, assigned about next Tuesday Midterm is tentatively scheduled for Thursday March 10 Read chapter 8
3
From last time... There are many cases where processes and threads want to access shared common variables, buffers, etc. Example: Producer and Consumer processes with a shared bounded buffer in between –Producer increments counter++ –Consumer decrements counter-- race conditions occur between producer and consumer –Machine-level instructions can be interleaved, resulting in unpredictable final value of shared counter variable
4
Synchronization // counter++ reg1 = counter; reg1 = reg1 + 1; counter = reg1; // counter--; reg2 = counter; reg2 = reg2 - 1; counter = reg2; ( 1) [5] ( 3) [6] ( 2) [5] ( 4) [4] ( 5) [6] ( 6) [4] Suppose we have the following sequence of interleaving, where the brackets [value] denote the local value of counter in either the producer or consumer’s process. Let counter=5 initially. At the end, counter = 4. But if steps (5) and (6) were reversed, then counter=6 !!! Value of shared variable counter changes depending upon (an arbitrary) order of writes - very undesirable!
5
Critical Section Some kernel data structures could be subject to race conditions, e.g. access to list of open files Kernel developer must ensure that no such race conditions occur User or kernel developer identifies critical sections in code where each process accesses shared variables –access to critical sections is controlled by special entry and exit code while(1) { entry section critical section (manipulate common var’s) exit section remainder code }
6
Critical Section Critical section access should satisfy multiple properties –mutual exclusion if process P i is executing in its critical section, then no other processes can be executing in their critical sections –progress if no process is executing in its critical section and some processes wish to enter their critical sections, then only those processes that are not executing in their remainder sections can participate in the decision on which will enter its critical section next this selection cannot be postponed indefinitely –bounded waiting there exists a bound, or limit, on the number of times other processes can enter their critical sections after a process X has made a request to enter its critical section and before that request is granted For most of the following slides, we will primarily be concerned with how to achieve mutual exclusion
7
Critical Section How do we protect access to critical sections? –want to prevent another process from executing while current process is modifying this variable - mutual exclusion –disable interrupts before entering critical section, disable interrupts after exiting critical section, reenable interrupts This provides mutual exclusion
8
Disabling Interrupts shared int counter; Code for p 1 Code for p 2disableInterrupts(); counter++;counter--;enableInterrupts(); Unfortunately, this approach to critical sections has many drawbacks: –Interrupts could be disabled arbitrarily long, e.g. the program may have an intentional or mistaken infinite loop while(1) –Interrupts can be disabled too long, e.g. if the critical section has many lines of code, then other process are blocked from executing until this process reenables interrupts Really only want to prevent p 1 and p 2 from interfering with one another; this blocks all p i overlapped I/O may be prevented
9
Critical Section Alternative: don’t disable interrupts for the entire time you’re in the critical section Alternative Solution: Set a variable/flag called a lock to indicate that the critical section is busy, then unset the flag when critical section is done –doesn’t disable interrupts in the critical section –a process P can now be interrupted in the critical section, but if it is, P still has the lock, so no other process will be able to acquire the lock and execute its critical code (provided all processes properly surround critical sections with entry and exit code)
10
First Try at Lock Implementation shared boolean lock = FALSE; shared int counter; Code for p 1 Code for p 2/* Acquire the lock */ while(lock) ; lock = TRUE;/* Execute critical sect */ counter++; counter--;/* Release lock */ lock = FALSE; modified version of Pearson slides
11
First Try at Lock Implementation shared boolean lock = FALSE; shared int counter; Code for p 1 Code for p 2/* Acquire the lock */ while(lock) ; lock = TRUE;/* Execute critical sect */ counter++; counter--;/* Release lock */ lock = FALSE; p1p1 p2p2 Blocked at while lock = TRUE lock = FALSE Interrupt modified version of Pearson slides If P1 is blocked on I/O and has the lock, then P2 blocks as expected (P2 busy waits until P1 unblocks its I/O and releases the lock)
12
First Try at Lock Implementation shared boolean lock = FALSE; shared double balance; Code for p 1 Code for p 2/* Acquire the lock */ while(lock) ; lock = TRUE;/* Execute critical sect */ counter++; counter--;/* Release lock */ lock = FALSE; This approach to protecting critical sections is unsafe, because it is subject to a race condition –If P1 is has just completed the test “while(lock)” and is just before the setting of “lock=TRUE”, P1 can get context switched out –Then P2 executes and sees lock=FALSE in its while(lock) test, and also drops through to the lock=TRUE statement –Now both P1 and P2 are executing in critical section - BAD
13
Atomic Lock Manipulation while (lock==TRUE) ; // test if lock taken lock = TRUE; // if not taken, set lock as taken test-and-set approach of the above two lines from the previous slide doesn’t work if process is interrupted right after while() instead, want the test-and-set process to be atomic, i.e. uninterruptable –Want an atomic instruction TestandSet() that tests shared variable lock then sets it Some hardware provides such an instruction TS
14
Atomic Lock Manipulation shared boolean lock = FALSE; shared int counter; Code for p 1 Code for p 2/* Acquire the lock */ while(TestandSet(&lock)) ;/* Execute critical sect */ counter++; counter--;/* Release lock */ lock = FALSE; ______________________________________________________________ boolean TestandSet(boolean *target) { // this is atomic, and the detailed parts of TestandSet are shown here boolean rv = *target; *target = TRUE; return rv; }
15
Atomic Lock Manipulation Mutual exclusion is achieved - no race conditions –If one process X tries to obtain the lock while another process Y already has it, X will wait in the loop –If a process is testing and/or setting the lock, no other process can interrupt it The system is exclusively occupied for only a short time - the time to test and set the lock, and not for entire critical section –only about 10 instructions don’t have to disable and reenable interrupts - time- consuming disadvantage: –requires user to put in while() –requires special hardware support in the form of a low level atomic machine instruction TS
16
Atomic Lock Manipulation Acquire(lock) {Release(lock) { disableInterrupts(); /* Loop until lock is TRUE */ lock = FALSE; while(lock) { enableInterrupts(); /* Let interrupts occur */} enableInterrupts(); disableInterrupts(); } lock = TRUE; enableInterrupts(); } Here’s another intuitive implementation of locks that doesn’t require a special hardware instruction - uses just disabling and reenabling of interrupts Bounds the amount of time that interrupts are disabled to only lock manipulation, not the entire critical section
17
Semaphores more general solution to mutual exclusion Semaphore S is an integer variable that, apart from initialization, is accessed only through 2 standard atomic operations –P(), also called wait(), short for Dutch word proberen “to test” somewhat equivalent to a test-and-set, but also involves decrementing the value of S –V(), also called signal(), short for Dutch word verhogen “to increment” increments the value of S –OS provides ways to create and manipulate semaphores atomically
18
Semaphores Pseudo-code for classic semaphore –its value can’t go below zero, i.e. classic semaphore is non-negative P(S) { while(S<=0) {wait or no op;} S--; } V(S) { S++; } atomic “atomic” P() is “atomic” in the sense that it tests S atomically, and if S<=0, then the process calling P() will relinquish control. Otherwise, the process continues forward and decrements S atomically. See Next Slide for implementation.
19
Semaphores Here’s an intuitive implementation of semaphores using only disabling and reenabling of interrupts –See the text for an example of implementing semaphores using TestandSet() instructions V(S) { disableInt(); S++; enableInt() } P(S) { disableInterrupts(); while(S==0) { enableInt(); disableInt(); } S--; enableInt() }
20
Semaphores Usage example: Suppose the initial value of S is S init = 1 –The first process X that calls P() on semaphore S will check if S<=0 (it is not), and then will decrement S to 0 this is performed atomically –If a 2 nd process Y calls P() for semaphore S, then Y will block on the semaphore, because now S=0, which satisfies the while test of S<=0 other processes that call P(S) will also block on the semaphore –When process X is done, X will V() the semaphore S, incrementing S atomically to 1 –Process Y will now check the semaphore and see that its value is now 1, so it can jump out of the while() loop and decrement S back to 0, thus completing its original P(S) Mutual exclusion is achieved
21
Semaphores Usage example: mutual exclusion Semaphore S = 1; // initial value of semaphore is 1 int counter; // assume counter is set correctly somewhere in code Process P1: P(S); // execute critical section counter++; V(S); Process P2: P(S); // execute critical section counter--; V(S); Both processes atomically P() and V() the semaphore S, which protects access to critical section code, in this case access to the shared variable counter
22
Semaphores The previous example showed how to use the semaphore as a binary semaphore –its initial value was set to 1 –a binary semaphore is also called a mutex lock, i.e. it can be used to provide mutual exclusion on some piece of critical code A semaphore can also be used more generally as a counting semaphore –its initial value is n, e.g. n=10 –the value of the semaphore is used to store the value of a shared variable, or is used to keep track of the number of processes allowed to access some shared resource
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.