Upload
geoffrey-daniels
View
223
Download
0
Embed Size (px)
Citation preview
04/18/23 104/18/23 104/18/23 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
04/18/23 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
04/18/23 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
04/18/23 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
04/18/23 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
04/18/23 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
1. Index in: next free position in buffer array
2. Index out: first filled position in buffer array
04/18/23 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
04/18/23 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
04/18/23 9
Bounded-Buffer – Shared-Memory Solution
04/18/23 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:00 2008”
• Consumer invokes (calls) the remove() method– Takes an item (Date object) from the buffer
04/18/23 11
Bounded-Buffer - insert() method
• Producer calls this method
04/18/23 12
Bounded-Buffer - remove() method
• Consumer calls this method
04/18/23 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
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
04/18/23 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
04/18/23 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)
04/18/23 17
Race Condition• ++count could be implemented in the CPU
as– register1 = count
register1 = register1 + 1count = register1
• --count could be implemented in the CPU as– register2 = count
register2 = register2 - 1count = register2
04/18/23 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
04/18/23 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
Critical-Section Problem1. Race Condition - When there is concurrent access to
shared data and the final outcome depends upon order of execution.
2. Entry Section - Code that requests permission to enter its critical section.
3. Critical Section - Section of code where shared data is accessed.
4. Exit Section - Code that is run after completing the critical section to signal that the process has finished running its critical section
5. Remainder Section - Code that is run after completing the exit section
04/18/23 20
04/18/23 21
Structure of a Typical Process
04/18/23 22
Solution to Critical-Section Problem
• Solution to critical-section problem must satisfy the following three requirements
1. 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
04/18/23 23
Solution to Critical-Section Problem
• Solution to critical-section problem must satisfy the following three requirements
2. 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
Solution to Critical-Section Problem
• Solution to critical-section problem must satisfy the following three requirements
3. 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
04/18/23 24
04/18/23 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
04/18/23 26
Peterson’s Solution• Peterson’s Solution solves the Critical-
Section Problem for TWO process only– 2 processes (P0 and P1) share 2 variables
1. int turn;
2. 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
Peterson’s Solution for Process Pi
04/18/23 27
04/18/23 28
Peterson’s Solution• Peterson’s Solution continued
– 2 processes (P0 and P1) share 2 variables:1. int turn;
2. 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
04/18/23 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
04/18/23 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
Peterson’s Solution for Process Pi
04/18/23 31
04/18/23 32
Algorithm Description• Initial values:
boolean readyFlag[0]=false;
boolean readyFlag[1]=false;
int turn=1;
04/18/23 33
Algorithm Description• Code for P1while(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 P0while(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
}
04/18/23 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)
04/18/23 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
04/18/23 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)
04/18/23 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
Critical Section Using Locks• General solution to the critical-section
problem emphasizing the use of locks to prevent race conditions
• Algorithm:
04/18/23 38
Synchronization Hardware• Many systems provide hardware support for
critical section code– This makes programming easier– Also makes the overall system more efficient
04/18/23 39
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
04/18/23 40
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 = countregister1 = register1 + 1count = register1
04/18/23 41
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
04/18/23 42
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
04/18/23 43
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
04/18/23 44
Semaphore as General Synchronization Tool
• This code uses a binary semaphore (where value == 0 or value == 1) to control access to the critical section
04/18/23 45
Semaphore Types1. Counting semaphore
– Integer value can range over an unrestricted domain
2. 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
04/18/23 46
Semaphore Implementation• Implementation of acquire()
• Implementation of release()
04/18/23 47
Semaphore Implementation• Two more operations
1. block() – place the process invoking the operation on the appropriate waiting queue• suspend process invoking it (wait)
2. wakeup() – remove one of processes in the waiting queue and place it in the ready queue• resume one process
04/18/23 48
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
04/18/23 49
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
04/18/23 50
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();
04/18/23 51
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
1. P0 executes S.acquire()
2. P1 executes Q.acquire()
3. P0 executes Q.acquire(), so P0 must wait until P1 executes Q.release()
4. P1 executes S.acquire(), so P1 must wait until P0 executes Q.release()
04/18/23 52
04/18/23 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
04/18/23 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
04/18/23 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
04/18/23 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
Classical Problems of Synchronization
• These are three of many concurrency-control (synchronization) problems
1. Bounded-Buffer Problem
2. Reader-Writer Problem
3. 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
04/18/23 57
04/18/23 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
04/18/23 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];
04/18/23 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
04/18/23 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
04/18/23 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
Semaphore Implementation• Implementation of acquire()
• Implementation of release()
04/18/23 63
04/18/23 64
Bounded-Buffer Problem• See link
to examplecode Solution.java
04/18/23 65
Bounded-Buffer Problem• Method insert() called by the Producer
04/18/23 66
Bounded-Buffer Problem• Method remove() called by the Consumer
04/18/23 67
Bounded-Buffer Problem• The structure of the producer process
04/18/23 68
Bounded-Buffer Problem• The structure of the consumer process
04/18/23 69
Bounded-Buffer Problem• The Factory
– See Solution.java on class web page for modified code
04/18/23 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
04/18/23 71
Readers-Writer Problem• Have two different kinds of processes
1. Readers• Only read from the database• They do NOT perform any updates
2. 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
04/18/23 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
04/18/23 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
04/18/23 74
Reader-Writer Problem• Interface for read-write locks
04/18/23 75
Reader-Writer Problem• Methods called by Readers
– public void acquireReadLock(int readerNum) {mutex.acquire();++readerCount;if (readerCount == 1){ db.acquire();}mutex.release();}
04/18/23 76
Reader-Writer Problem• Methods called by Readers
– public void releaseReadLock(int readerNum) {mutex.acquire(); --readerCount;if (readerCount == 0){ db.release();}mutex.release();}
04/18/23 77
Reader-Writer Problem• Methods called by Writers
04/18/23 78
Reader-Writer Problem • The structure of a Writer process
04/18/23 79
Reader-Writer Problem • The structure of a Reader process
04/18/23 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
04/18/23 81
Reader-Writer Problem• Who has priority in accessing data?
– Readers• No Reader will be kept waiting unless a Writer has
attained permission
04/18/23 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
04/18/23 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
04/18/23 84
Dining-Philosophers Problem• Diagram of table layout• See link from
class web sitefor animatedexample
04/18/23 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
04/18/23 86
Dining-Philosophers Problem• Solution for Philosopher i
– Each philosopher picks up left chopstick, then picks up right chopstick
04/18/23 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
04/18/23 88
Dining-Philosophers Problem• These solutions prevent deadlock, but still
have the possibility of starvation1. Have only 4 philosophers and 5 chopsticks
2. Only pick up chopsticks if both are available
3. Odd philosophers first pick up left chopstick, while even philosophers first pick up right chopstick
04/18/23 89
Synchronization Examples1. Solaris
2. Windows XP
3. Linux
04/18/23 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)
04/18/23 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
04/18/23 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
04/18/23 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