Download presentation
Presentation is loading. Please wait.
1
Process Synchronization
ICS 240: Operating Systems Instructor: William McDaniel Albritton Information and Computer Sciences Department at Leeward Community College Original slides by Silberschatz, Galvin, and Gagne from Operating System Concepts with Java, 7th Edition with some modifications Also includes material by Dr. Susan Vrbsky from the Computer Science Department at the University of Alabama 4/25/2017 4/25/2017 4/25/2017 1 1
2
Background Cooperating and Concurrent Processes
Executions overlap in time and they need to be synchronized Cooperating processes may share a logical address space (such as the same code and data segments) as well as share data through files or messages Concurrent access to shared data may result in data inconsistency Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes 4/25/2017
3
Example Suppose that we wanted to provide a solution to the consumer-producer problem that has a single shared buffer We can do so by having an integer count that keeps track of the size of the buffer (an array) Initially, count is set to 0 count is incremented by the producer after it inserts a new object into the buffer array and is decremented by the consumer after it removes an object from the buffer 4/25/2017
4
Simulating Shared Memory in Java
Both the Produce and Consumer share the same BoundedBuffer object Emulates shared memory, as Java does not support shared memory 4/25/2017
5
Producer-Consumer Problem
Paradigm for cooperating processes, producer process produces information that is consumed by a consumer process unbounded-buffer places no practical limit on the size of the buffer Used in Factory.java example program in Chapter 4 on Threads bounded-buffer assumes that there is a fixed buffer size Used in Factory.java example program for Chapter 6 on Process Synchronization 4/25/2017
6
Producer-Consumer Problem
Produce & Consumer objects share the same BoundedBuffer object Class BoundedBuffer has a buffer array which is an array of Objects So the buffer array can hold any type of object Class BoundedBuffer is implemented as a circular array (buffer) with two indexes Index in: next free position in buffer array Index out: first filled position in buffer array 4/25/2017
7
Producer-Consumer Problem
Class BoundedBuffer has a count variable Keeps track of the number of items currently in the buffer array Variable BUFFER_SIZE is the maximum size of the buffer array Buffer array is empty if count==0 Buffer array is full if count==BUFFER_SIZE while loop is used to block the producer & consumer when they cannot use the buffer array 4/25/2017
8
Bounded-Buffer – Shared-Memory Solution
An interface for buffers Interfaces are used to enforce the method names of a class Makes programs more flexible 4/25/2017
9
Bounded-Buffer – Shared-Memory Solution
4/25/2017
10
Producer-Consumer Problem
Producer invokes (calls) the insert() method Puts an item into the buffer In the program, the item is a Date object Class java.util.Date represents a specific instant in time, with millisecond precision The toString() method has format: “dow mon dd hh:mm:ss zzz yyyy” For example: “Wed Mar 12 22:30:09 GMT-10: ” Consumer invokes (calls) the remove() method Takes an item (Date object) from the buffer 4/25/2017
11
Bounded-Buffer - insert() method
Producer calls this method 4/25/2017
12
Bounded-Buffer - remove() method
Consumer calls this method 4/25/2017
13
Java Threads Java threads are managed by the JVM (Java Virtual Machine) The JVM is can be thought of as a software computer that runs inside a hardware computer Java threads may be created by: Implementing the Runnable interface 4/25/2017
14
Java Thread Methods & States
The start() method allocates memory for a new thread in the JVM, and calls the run() method The sleep() method causes the currently executing thread to sleep (cease execution) for the specified number of milliseconds
15
Producer-Consumer Problem
In the Factory.java example program, the methods insert() and remove() called by the producer and consumer may not function correctly when the methods are executed concurrently This is because of something called a race condition 4/25/2017
16
Race Condition A race condition is a situation in which 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 In other words, outcome depends on order in which the instructions are executed To understand how this works, we need to think a little about machine language, which manipulates the registers in the CPU When we compile a program, this converts the source code (Java code in the *.java file) to machine code (bytecode in the *.class file) 4/25/2017
17
Race Condition ++count could be implemented in the CPU as
register1 = count register1 = register1 + 1 count = register1 --count could be implemented in the CPU as register2 = count register2 = register2 - 1 count = register2 4/25/2017
18
Race Condition Consider this arbitrary order (note that other combinations are possible) of execution with initial value of count = 5 S0: producer register1 = count {register1 = 5} S1: producer register1 = register1 + 1 {register1 = 6} S2: consumer register2 = count {register2 = 5} S3: consumer register2 = register2 – 1 {register2 = 4} S4: producer count = register1 {count = 6 } S5: consumer count = register2 {count = 4} We end up with the incorrect value of count = 4 Other combinations of statements can also give us correct or incorrect results 4/25/2017
19
Race Condition To prevent incorrect results when sharing data, we need to make sure that only one process at a time manipulates the shared data In this example, the variable count should be accessed and changed only by one process at a time 4/25/2017
20
Critical-Section Problem
Race Condition - When there is concurrent access to shared data and the final outcome depends upon order of execution. Entry Section - Code that requests permission to enter its critical section. Critical Section - Section of code where shared data is accessed. Exit Section - Code that is run after completing the critical section to signal that the process has finished running its critical section Remainder Section - Code that is run after completing the exit section 4/25/2017
21
Structure of a Typical Process
4/25/2017
22
Solution to Critical-Section Problem
Solution to critical-section problem must satisfy the following three requirements Mutual Exclusion If a process is executing in its critical section, then no other processes can be executing in their critical sections In other words, only one process can enter its critical section at a time Assume that each process has one critical section, and that several processes have the same critical section 4/25/2017
23
Solution to Critical-Section Problem
Solution to critical-section problem must satisfy the following three requirements Progress If no process is executing in its critical section and there exist some processes that wish to enter their critical section, then the selection of the processes that will enter the critical section next cannot be postponed indefinitely In other words, a decision on which process will be next must be made only by the processes that are trying to enter their critical section So should make progress on entering critical-section 4/25/2017
24
Solution to Critical-Section Problem
Solution to critical-section problem must satisfy the following three requirements Bounded Waiting A bound must exist 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 In other words, once a process wants to get into its critical section, other processes are restricted in the number of times they can get into their critical sections So should be a bound on how long have to wait 4/25/2017
25
Solution to Critical-Section Problem
If your solution satisfies the 3 requirements: Mutual exclusion, progress, and bounded waiting You will have no: Starvation there exist a process who never gets into the critical section Deadlock two or more processes waiting for an event that will not occur 4/25/2017
26
Peterson’s Solution Peterson’s Solution solves the Critical-Section Problem for TWO process only 2 processes (P0 and P1) share 2 variables int turn; boolean readyFlag[2]; The variable turn indicates whose turn it is to enter its critical section If turn == 0, then P0 is allowed to enter its critical section If turn == 1, then P1 is allowed to enter its critical section 4/25/2017
27
Peterson’s Solution for Process Pi
4/25/2017
28
Peterson’s Solution Peterson’s Solution continued
2 processes (P0 and P1) share 2 variables: int turn; boolean readyFlag[2]; The readyFlag array is used to indicate if a process is ready to enter its critical section. If readyFlag[0] == true, the process P0 is ready to enter its critical section readyFlag[i]=true implies that Pi is either ready to enter the critical section, or running its critical section If readyFlag[0] == false, the process P0 has finished its critical section 4/25/2017
29
Algorithm Description
Algorithm for Peterson’s Solution When Pi (where i is 0 or 1) is ready to enter the critical section Pi assigns readyFlag[i] = true This statement says that this process wants to enter its critical section Pi assigns turn = j (where j is the other process, so j=1-i) This statement allows the other process to enter its critical section 4/25/2017 29
30
Algorithm Description
Comments on the algorithm If both processes try to enter their critical sections at the same time, then turn will be set to 0 or 1 at roughly the same time Since both processes share the turn variable, only one assignment will last, as one process will quickly overwrite the value from the other process For example, P0 wants to enter its critical section, so turn=1 In the next nanosecond, P1 wants to enter its critical section, so turn=0 So in this case, P0 is the allowed to enter its critical section first 4/25/2017 30
31
Peterson’s Solution for Process Pi
4/25/2017
32
Algorithm Description
Initial values: boolean readyFlag[0]=false; boolean readyFlag[1]=false; int turn=1; 4/25/2017 32
33
Algorithm Description
Code for P1 while(true){ readyFlag[1]=true; //P1 ready turn=0; //P0 can go while(readyFlag[0]==true && turn==0){ //do nothing } //critical section readyFlag[1]=false; //done //remainder section } Code for P0 while(true){ readyFlag[0]=true; //P0 ready turn=1; //P1 can go while(readyFlag[1]==true && turn==1){ //do nothing } //critical section readyFlag[0]=false; //done //remainder section } 4/25/2017 33
34
Correctness of Solution
Criteria 1: mutual exclusion Pi can only enter its critical section if either readyFlag[j]==false or turn==i Either case will make the 2nd while statement false, so the process can enter its critical section If both processes want to enter their critical sections at the same time, both readyFlag[0]==true and readyFlag[1]==true, but either turn==0, or turn==1, so only one process at one time can enter its critical section (mutual exclusion) 4/25/2017 34
35
Correctness of Solution
Criteria 2 & 3: process and bounded waiting Pi can be prevented from entering its critical section only if it is stuck in the 2nd while loop with readyFlag[j]==true and turn==j If Pj does not want to enter its critical section, then readyFlag[j]==false, so Pi can then enter its critical section If Pj does want to enter its critical section, then readyFlag[j]==true & either turn==i or turn==j If turn==i, then Pi will enter its critical section If turn==j, then Pj will enter its critical section 4/25/2017 35
36
Correctness of Solution
Criteria 2 & 3: process and bounded waiting When Pj exits its critical section Pj will assign flag[j]=false, so Pi can enter its critical section If Pj want to enter its critical section again, then it will assign flag[j]=true and turn=i So Pi can enter its critical section Therefore, Pi will eventually enter its critical section (progress) after waiting for Pj to enter and finish its critical section at most one time (bounded waiting) 4/25/2017 36
37
Locks Solutions to the critical-section problem all have one minimum requirement This requirement is a lock Locks prevent race conditions This is because a process must acquire the lock before entering its critical section and release the lock after exiting its critical section 4/25/2017 37
38
Critical Section Using Locks
General solution to the critical-section problem emphasizing the use of locks to prevent race conditions Algorithm: 4/25/2017
39
Synchronization Hardware
Many systems provide hardware support for critical section code This makes programming easier Also makes the overall system more efficient 4/25/2017
40
Synchronization Hardware
Uniprocessors (single processor system) When shared variables are being modified, the processor disables interrupts Currently running code executes without preemption Unfortunately, this approach is inefficient on multiprocessor systems Reason is because messages have to be passed to all processors whenever shared variables are being modified All these excess messages slow down the operating system 4/25/2017
41
Synchronization Hardware
Modern machines provide special atomic hardware instructions Atomic means that a certain group of instructions cannot be interrupted In other words, several instructions form one uninterruptible unit For example, the machine instructions for ++count can be implemented atomically in the CPU as one uninterruptible unit register1 = count register1 = register1 + 1 count = register1 4/25/2017
42
Semaphore Invented by Edsger W. Dijkstra in 1965
Very famous Dutch computer scientist Invented many algorithms as well as helped to ban the GOTO statement For example, you may have studied Dijkstra’s algorithm, which is the shortest path problem “Computer Science is no more about computers than astronomy is about telescopes.” Focused on theory of computer science Programmers should use every trick and tool possible for the complex task of programming 4/25/2017
43
Semaphore A semaphore is a synchronization tool that does not require busy waiting Busy waiting (spinning, or spinlock) is continual looping Continually checks to see if a condition is true For example, a process that is waiting for a lock to become available is doing busy waiting Semaphores are simple and powerful Can be used to solve wide variety of synchronization problems 4/25/2017
44
Semaphore A semaphore has (1) an integer variable (value) and (2) a queue of processes The integer variable (value) is modified by two atomic methods: acquire() and release() Originally called P() and V() P = probern = Dutch for “to test” V = verhogun = Dutch for “to increment” acquire() is used before the critical section to prevent access if other processes are using it release() is used after the critical section to allow other processes to access it 4/25/2017
45
Semaphore as General Synchronization Tool
This code uses a binary semaphore (where value == 0 or value == 1) to control access to the critical section 4/25/2017
46
Semaphore Types Counting semaphore Binary semaphore
Integer value can range over an unrestricted domain Binary semaphore Integer value can only be 0 or 1 Can be simpler to implement Also known as mutex locks Mutex is short for mutual exclusion 4/25/2017
47
Semaphore Implementation
Implementation of acquire() Implementation of release() 4/25/2017
48
Semaphore Implementation
Two more operations block() – place the process invoking the operation on the appropriate waiting queue suspend process invoking it (wait) wakeup() – remove one of processes in the waiting queue and place it in the ready queue resume one process 4/25/2017
49
Possible Problems with Semaphore
Prone to programmer errors For example, by switching acquire() and release() methods by mistake Starvation is possible Starvation is indefinite blocking of a process For example, a process may never be removed from the semaphore queue in which it is waiting 4/25/2017
50
Possible Problems with Semaphore
May have deadlock if don't have synchronization specified correctly Deadlock is when two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes 4/25/2017
51
Deadlock Example Let S and Q be two binary semaphores both initialized to value=1 P0 and P1 are two processes P0 P1 S.acquire(); Q.acquire(); Q.acquire(); S.acquire(); . . S.release(); Q.release(); Q.release(); S.release(); 4/25/2017
52
Deadlock Example These steps cause the two processes to be deadlocked, as they are waiting for the other process to release a resource that cannot be released P0 executes S.acquire() P1 executes Q.acquire() P0 executes Q.acquire(), so P0 must wait until P1 executes Q.release() P1 executes S.acquire(), so P1 must wait until P0 executes Q.release() 4/25/2017
53
Problems with Semaphores
Correct use of semaphore operations Variable mutex (mutual exclusion) is a semaphore with value=1 mutex.acquire(); critical section; mutex.release(); Method acquire() should always be used before entering a critical section Method release() should always be used after exiting a critical section If this order is not used, then will have synchronization errors 4/25/2017 53
54
Problems with Semaphores
INCORRECT use of semaphore operations mutex.release(); critical section; mutex.acquire(); When method acquire() and release() are reversed, several process will be able to execute in their critical section at the same time, so mutual exclusion will not be possible This error is difficult to detect, because we must wait for several processes to execute their critical sections at the same time, which may not always happen when we run the processes 4/25/2017 54
55
Problems with Semaphores
INCORRECT use of semaphore operations mutex.acquire(); critical section; mutex.acquire(); When method acquire() is placed both before and after the critical section, deadlock will occur In this case, even a single process can deadlock itself 4/25/2017 55
56
Problems with Semaphores
INCORRECT use of semaphore operations critical section; mutex.release(); When method acquire() is not used, mutual exclusion will not be possible mutex.acquire(); critical section; When method remove() is not used, deadlock will happen In this case, even a single process can deadlock itself 4/25/2017 56
57
Classical Problems of Synchronization
These are three of many concurrency-control (synchronization) problems Bounded-Buffer Problem Reader-Writer Problem Dining-Philosophers Problem These problems are used to test any newly proposed synchronization solutions For these three problems, we will apply semaphores as a solution 4/25/2017
58
Bounded-Buffer Problem
Both the Produce and Consumer share the same BoundedBuffer object Emulates (simulates) shared memory, as Java does not support shared memory 4/25/2017 58 58
59
Bounded-Buffer Problem
BUFFER_SIZE is the number of items that can be stored in a buffer In the example program Solution.java on the class web site, BUFFER_SIZE = 3 The buffer is an array of Objects Objects is used, so the array can potentially store any type of object, such as String, Integer, Date, etc. Object [] buffer = new Object[BUFFER_SIZE]; 4/25/2017
60
Bounded-Buffer Problem
Semaphore mutex (mutual exclusion) initialized to value = 1 This provides mutual exclusion Since value=1, only one process at one time can modify count or modify the data in the buffer array In other words, either the producer or consumer can enter the critical section at one time, but both cannot enter the critical section at the same time 4/25/2017 60
61
Bounded-Buffer Problem
Semaphore empty initialized to value=BUFFER_SIZE Variable empty is used to keep track of the number of empty elements in the array This provides synchronization for the producer Makes producer stop running when buffer is full If buffer is full, then value=0 for semaphore empty 4/25/2017 61
62
Bounded-Buffer Problem
Semaphore full initialized to value=0 Variable full is used to keep track of the number of occupied elements in the array This provides synchronization for consumer Makes consumer stop running when buffer is empty If buffer is empty, then value=0 for semaphore full 4/25/2017 62
63
Semaphore Implementation
Implementation of acquire() Implementation of release() 4/25/2017
64
Bounded-Buffer Problem
See link to example code Solution.java 4/25/2017
65
Bounded-Buffer Problem
Method insert() called by the Producer 4/25/2017
66
Bounded-Buffer Problem
Method remove() called by the Consumer 4/25/2017
67
Bounded-Buffer Problem
The structure of the producer process 4/25/2017
68
Bounded-Buffer Problem
The structure of the consumer process 4/25/2017
69
Bounded-Buffer Problem
The Factory See Solution.java on class web page for modified code 4/25/2017
70
Reader-Writer Problem
A data set is shared among a number of concurrent process Models access to a database For example, an airline reservation system has many processes that are trying to read from the database as well as write to the database When the database is being updated by a single Writer, Readers and other Writers should not have access to the database Otherwise, any number of Readers can have read from the database at one time 4/25/2017 70
71
Readers-Writer Problem
Have two different kinds of processes Readers Only read from the database They do NOT perform any updates Writers Can both read from and write to the database Problem Allow multiple Readers to read at same time Only one single Writer can access the shared data at the same time 4/25/2017 71
72
Reader-Writer Problem
Shared data is stored in the variable database of class Database Integer readerCount initialized to 0 This keeps track of the number of Readers Semaphore mutex initialized to 1 Enforces mutual exclusion when readerCount is incremented or decremented Semaphore db initialized to 1 Provides mutual exclusion to the database for the Writers Also used by Readers to prevent Writers from changing database when the database is being read 4/25/2017 72
73
Reader-Writer Problem
Each reader thread alternates between sleeping and reading When a Reader wants to read from the database, it calls database.acquireReadLock() method The first Reader calls db.acquire() to prevent any Writers from changing the database When a Reader has finished reading from the database, it calls database.releaseReadLock() method The last Reader calls db.release() to allow any Writer to change the database 4/25/2017 73
74
Reader-Writer Problem
Interface for read-write locks 4/25/2017 74
75
Reader-Writer Problem
Methods called by Readers public void acquireReadLock(int readerNum) { mutex.acquire(); ++readerCount; if (readerCount == 1){ db.acquire(); } mutex.release(); } 4/25/2017 75
76
Reader-Writer Problem
Methods called by Readers public void releaseReadLock(int readerNum) { mutex.acquire(); --readerCount; if (readerCount == 0){ db.release(); } mutex.release(); } 4/25/2017 76
77
Reader-Writer Problem
Methods called by Writers 4/25/2017 77
78
Reader-Writer Problem
The structure of a Writer process 4/25/2017 78
79
Reader-Writer Problem
The structure of a Reader process 4/25/2017 79
80
Reader-Writer Problem
See ReaderWriterSolution.java program on class web page with modified code For example, the textbook uses variable name db for both the Semaphore and Database classes In the example program, these variable names are changed to Semaphore db and Database database in order to make them distinguishable 4/25/2017 80
81
Reader-Writer Problem
Who has priority in accessing data? Readers No Reader will be kept waiting unless a Writer has attained permission 4/25/2017 81
82
Dining-Philosophers Problem
Many synchronization problems involve sharing a limited number of resources between several processes Dining-Philosophers Problem is represents a this kind of synchronization problem Conceived originally by Dijkstra For example, 5 computers having access to 5 shared tape drives 4/25/2017 82
83
Dining-Philosophers Problem
5 philosophers sitting at a table with 1 bowl of rice in center and 5 single chopsticks, with 1 single chopstick between each philosopher All they do is think and eat When they think, they do not eat, so they put down their chopsticks, one single chopstick at a time When they eat, they need to pick up BOTH chopsticks, one single chopstick at a time Cannot grab chopsticks being used by neighbor Can only use chopstick to immediate left or right 4/25/2017
84
Dining-Philosophers Problem
Diagram of table layout See link from class web site for animated example 4/25/2017
85
Dining-Philosophers Problem
One possible solution is to represent each chopstick with a semaphore Semaphore chopStick[5] Each element is initialized to 1 Before eating, each philosopher has to call two acquire() methods After eating (before thinking), each philosopher has to call two release() methods 4/25/2017 85
86
Dining-Philosophers Problem
Solution for Philosopher i Each philosopher picks up left chopstick, then picks up right chopstick 4/25/2017 86
87
Dining-Philosophers Problem
Problem with proposed solution Have possibility of deadlock If each philosopher is hungry at same time, all will pick up left chopstick So value=0 for each of the 5 semaphores Next when each philosopher attempts to pick up right chopstick, this will cause a delay forever Code cannot progress past the call to 2nd acquire() method 4/25/2017 87
88
Dining-Philosophers Problem
These solutions prevent deadlock, but still have the possibility of starvation Have only 4 philosophers and 5 chopsticks Only pick up chopsticks if both are available Odd philosophers first pick up left chopstick, while even philosophers first pick up right chopstick 4/25/2017 88
89
Synchronization Examples
Solaris Windows XP Linux 4/25/2017
90
Solaris Synchronization
Implements a variety of locks to support multitasking, multithreading, and multiprocessing Uses adaptive mutexes for efficiency when protecting data from short code segments An adaptive mutex is a kind of semaphore that either uses a spinlock if waiting on a running process (that should be done soon) or blocks the current process (by putting it to sleep) if waiting on a process that is not in a run state (that might not be done soon) 4/25/2017
91
Solaris Synchronization
On single processor systems The adaptive mutex always causes a thread to sleep, instead of using a spinlock Uses reader-writer locks when longer sections of code need access to data Uses turnstiles to order the list of threads waiting to acquire either an adaptive mutex or reader-writer lock A turnstile is a queue of threads blocked on a lock 4/25/2017 91
92
Windows XP Synchronization
To protect access to global resources On uniprocessor (single processor) systems, disables interrupts On multiprocessor systems, uses spinlocks to protect short code segments A thread cannot be preempted when holding a spinlock Also provides dispatcher objects which may act as semaphores Protects shared data by requiring a thread to use a mutex when accessing the shared data 4/25/2017
93
Linux Synchronization
On single processor systems disables interrupts to protect short critical sections On multiple processor systems uses spinlocks to protect short critical sections For long critical sections uses semaphores 4/25/2017
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.