You are on page 1of 61
Operating System Concepts Mahmood Bashir mahmood.itpk@gmail.com AR AAR Chapter 5: Process Synchronization Background = What is Cooperating Process? d = A process is cooperating if it can affect or be affected by other executing processes, e.g. sharing data with others. = How Cooperating processes share data? = Share a logical address space, both code and data( Shared Memory) ® Files or messages ( Message Passing ) = What are Concurrent Processes and Parallel Processes? = In Concurrent execution several instruction streams execute simultaneously on single processing core using time sharing. The CPU scheduler switches rapidl between processes, so one process may not complete execution before another one is scheduled. = In parallel execution several instruction streams execute simultaneously on separate processing cores. = Concurrent and parallel access to shared data may result in data inconsistency. = Maintaining data consistency requires mechanisms to ensure the orderly execution of cooperating processes Background Olllustration 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 counter that keeps track of the number of full buffers. Initially, counter 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. Producer / Consumer Idea If the buffer is partially full, producer or consumer can run: producer +L Cae leal consumer If the buffer is empty, only the producer can run: producer —-—}-—-—t tt | If the buffer is full, only the consumer can run: eld|c|bla consumer 4—_- Producer producer EFF | Another producer-consumer implementation allows BUFFER_SIZE items by using a counter initialized to 0. while (true) { /* produce an item in next produced */ while (counter == BUFFER SIZE) ; /* do nothing, wait since buffer is full */ buffer[in] = next_produced; in = (in + 1) % BUFFER_SIZE; counter++; Consumer ; e|djc|b consumer a while (true) ( while (counter == 0) ; /* do nothing since the buffer is empty*/ next_consumed = buffer [out]; out = (out + 1) % BUFFER SIZE; counter--; /* consume the item in next consumed */ (Although correct separately, producer-consumer may not function correctly when executed concurrently. 1 {painter te 3 bo prodiioes and eoneuner sonciirently exnnite fhe =+ ane, counter ma be 4 8, or é Race Condition = COUNCEL+F+ couldbe implemented in machine level by: registerl = counter registerl = register] +1 counter = registerl + registers isa local CPU register = COUNCEL—— couls ve impremented as register? = counter egister? = register? - 1 counter = register2 * register2is a local CPU register * Consider this execution interleaving with “count = 8" intially: $0: producer execute register = counter {register St: producer execute registeri = registerl + 1 — {registert 52: consumer execute register? = counter {register2 - 5} $3: consumer execute register? = register? - 1 {resister2-4) SA: producer execute counter = register! {counter = 6} 55: consumer execute counter = register? {counter = 4} Race Condition Because of the timing and which process starts first, There is a chance that different executions mayfen with different results k OR Occurs when multiple processes or threads read and write data items, The final result depends on the order of execution The code yields “counter while five buffers are full. Swapping the last statements yields “counter This is called a race condition. To guard against it only one process at a time should be manipulating counter. ‘The processes must be synchronized in some way. Any changes that result from threads sharing data running in parallel on different processing cores should not interfere with one another. Example: Kernel data structure maintaining list of open files, modified when new file is opened or closed. If two processes open files simultaneously, the separate updates could result in a race condition. Similarly, data structures of memory allocation, process lists, and interrupt handling, subject to race conditions Critical Section Problem ical Sectio d = Asection of code in which the process accesses and modifies shared variables = Consider system of n processes {py Py + P-1} = Each process has segment of code called critical section = Process may be changing common variables, updating table, writing file, etc = When one process in critical section, no other may be in its critical section = Critical section problem is to design a cooperation protocol where each process must request permission to enter its critical section = The request code section is called the entry section. The critical section is followed by an exit section. The remaining code is the remainder section. Critical Section FR = General structure of process P, do { entry section critical section exit section remainder section } while (true); Algorithm for Process P; FR critical remainder section ) while (true); do ( Solution to Critical-Section Probien Sy The protocol should satisfy: d 1. Mutual Exclusion - If process P, 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 the selection of the processes that will enter the critical section next cannot be postponed indefinitely 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 Example: Kernel data structure maintaining list of open files, modified when new file is opened or closed. If two processes open files simultaneously, the separate updates could result in a race condition Aeenters critical region A wsisecieca Process &§ 1 a 1 ' Battomptsto ; Benters | — Bleaves 1 i 1 fi enter critical critical region critical region region T 1 1 + Blocked 4 Tt Ts A Process B dee Tm ——> Mutual exclusion using critical regions. Mutual Exclusion Problem : Starvation LY = Also known as Indefinite Postponement <3 = Definition = Indefinitely delaying the scheduling of a process in favour of other processes = Cause = Usually a bias in a systems scheduling policies (a bad scheduling algorithm) = Solution = Implement some form of aging Another Problem : Deadlocks =< = Two (or more) processes are blocked waiting for an event that will’ never occur = Generally, A waits for B to do something and B is waiting for A = Both are not doing anything so both events never occur How to Implement Mutual Exclusion Three possibii = OS:Extending hardware solutions to provide some functions and data structure support to the programmer = Application: Programmer builds some method into the program = Hardware: Special H/W instructions provided to implement ME = All schemes rely on some code for * enter_critical_section, and * exit_critical_section = These "functions" enclose the critical section Critical-Section Handling in OS Two approaches depending on if kernel is preemptive or non- preemptive ™Preemptive— allows a process to be preempted while it is running in kernel mode. = Non-preemptive — process runs until it exits kernel-mode, blocks, or voluntarily yields control of the CPU. * A non-preemptive kernel is race conditions free, only one process is active in the kernel at a time. = Preemptive kernels must be carefully designed to ensure that shared kernel data are free from race conditions. = Why a preemptive kernel is favored? ® It is more responsive, since a kernel-mode process will not run for an arbitrarily long period. Suitable for réal-time programming. Peterson’ s Solution = ASW-based solution to the critical-section problem is Peterson’s solution. * It is restricted to two processes alternating execution of their critical and remainder sections. * No correctness is guaranteed for modern computer architectures, because of how they perform basic machine-language instructions. = Assume that the load and store machine-language instructions are atomic; that is, cannot be interrupted = The two processes share two variables: * intturn; * Boolean flag[2] = The variable turn indicates whose turn it is to enter the critical section ® The flag array is used to indicate if a process is ready to enter the critical section. ™ flag[i] = true indicates that process P, is ready to enter its critical section = Ifturn == j P; is allowed to execute in its critical section. = To enter the critical section, P; first sets flag[i] = true and then turn = j , asserting that if P; wishes to enter the critical section, it can do so. int turn=0; boolean flag[2]; flag[0|=false; flag[1=false; do { flagli] = true; entry res = 33 section while (flag[j] && turn == j); critical section false flagl remainder section } while (true); null statement The structure of P; in Peterson's solution. int turn=0; boolean flag(2]; flag[0|=false; flag[1=false; do { flag[j] = true; entry turn =i; section while (flag[i] && turn ==i); critical section flagljl = false; remainder section } while (true); null statement exit section The structure of P; in Peterson’s solution. Peterson’ s Solution (Cont.) A = If P; and P; try to enter at the same time, turn sets toiand je = %: at roughly the same time. = One will last, the other is overwritten immediately. = The eventual turn determines which processes is allowed to enter its critical section first. = Provable that the three CS requirement are met: 1. Mutual exclusion is preserved 2. Progress requirement is satisfied 3. Bounded-waiting requirement is met Peterson’ s Solution (Cont.) we) Mutual exclusion: d = P; enters its critical section only if either flag[j] == false or turn == i. = if both want to enter in critical sections at the same time,means flag[i] == true, flag[j] == true. = But it is impossible that turn == i and turn == j togather "if both want to enter in critical sections at the same time, then value of turn will decide which process will enter its critical section. Peterson’ s Solution (Cont.) = Progression and bounded waiting: P; is prevented from entering critical d section only if it is stuck in the while loop with flag[j] == true and turn ==]. * Let us consider the various stages P; can be. " If P; is not ready to enter the critical section, then flag[j] == false, and P; can enter its critical section. ® If P; has set flag|j] = true and is also executing in its while statement, then either turn i or turn = If turn == i, then P; will enter the critical section. If turn critical section (progression) . * Once P; exits its critical section, it will reset flag|j] = false , allowing P; to enter its critical section. "If P; sets flag[j] = true again, it must also set turn = i, = Thus, since P; does not change the value of turn in the while loop, P; will enter the critical section (progress) after at most one entry by P; (bounded waiting). j, then P; enters Synchronization Hardware FR = Many systems provide hardware support for implementing « ©: the critical section code. = All solutions below based on idea of locking = Protecting critical regions via locks = Uniprocessors — could disable interrupts = Modern machines provide special atomic hardware instructions = Atomic = non-interruptible = Either test memory word and set value = Or swap contents of two memory words Solution to Critical-section Problem Using SD do { a critical section lrelease lock remainder section } while (TRUE); Synchronization Hardware = Interrupt Disabling: Uniprocessor = Uniprocessors — could disable interrupts = disabling interrupts guarantees mutual exclusion = Currently running code would execute without preemption = Disadvantages: = The efficiency of execution could be noticeably degraded ® This approach will not work ina multiprocessor architecture = Why disabling interrupts is not a feasible solution in Multiprocessor Systems? = Disabling interrupts on a multiprocessor can be time consuming, since the message is passed to all the processors. This message passing delays entry into each critical section, and system efficiency decreases. test_and_set Instruction FR boolean test_and_set (boolean *target) Definition: boolean rv = *target; *target = TRUE; return rv: ) = Executed atomically = If two test and set() instructions are executed simultaneously (each on a different CPU), they will be executed sequentially in some arbitrary order = Returns the original value of passed parameter = Set the new value of passed parameter to “TRUE”. Solution using test_and_set() FP = Shared Boolean variable lock, initialized to FALSE ad = Solution: while (test_and_set (élock)) + /* do nothing +/ /* cxitical section */ lock = false; /* remainder section */ ) while (true) ; compare_and_swap Instruction FP Definition: int compare _and_swap(int *value, int expected, int newvalue) ( tne temp = value; Af (*value == expected) value = new_value; return temp; ) 1.Executed atomically 2.Returns the original value of passed parameter “value” 3.Set the variable “value” the value of the passed parameter “new_value” but only if “value” ==“expected”. That is, the swap takes place only under this condition. Solution using compare and. swap_fGDY a "Shared integer “lock” initialized to 0; *Solution: 0, 1) '= 0) /* critical section */ lock = 0; /* remainder section */ } while (true); Mutex Locks = Previous solutions are complicated and generally inaccessible to application programmers = 0S designers build software tools to solve critical section problem = Simplest is MUtEX lock (short for mutual exclusion). = Aprocess must acquire the lock before entering a critical section, releasing it upon exits. * Calls to acquire () and release () must be atomic = Usually implemented via hardware atomic instructions do { acquire lock Solution to the critical section critical-section release lock problem using remainder section mutex locks. } while (true); =A mutex lock has a Boolean variable available whose value indicates if the lock is available or not. "If the lock is available, a call to acquire() succeeds, and the lock is then considered unavailable. =A process that attempts to acquire an unavailable lock is blocked until the lock is released. acquire() { while (!available) ; /* busy wait */ available = false;; ) release() { available = true; } =Calls to acquire() or release() are performed atomically (no interrupts), requiring dedicated HW mechanisms. "= Disadvantage: = A disadvantage of the busy waiting is the continuous looping, wasting CPU cycles that other process may use productively. = This lock therefore called a spinlock Semaphore Pm = Synchronization tool that provides more sophisticated ways (than i Mutex locks) for process to synchronize their activities. = Semaphore S$ — shared integer variable among many processes = Two standard operations to modify S ="wait() andsignal () = Originally called P() and V() "Or called down() and up() = Can only be accessed via two indivisible (atomic) operations "Wait(s) "Signal (s) Semaphore = Definition of the wait() operation wait(s) { while (S <= 0) i // busy wait S--; } = Definition of the signai() operation signal(s) { S++; } = Wait and signal must be executed without interruption (atomic). Semaphore Usage- Mutual Exclusion = Two types of semaphores — Binary and Counting semaphore = Binary semaphore —having integer value can range only between 0 and 1 = Same as a mutex lock — can be used to solve critical section problem = A semaphore variable (lets say mutex) can be shared by N processes, and initialized to 1. = Each process is structured as follows: do{ wait (mutex); // Critical Section signal (mutex); // remainder section } while (TRUE); Usage : Mutual Exclusion Process 0 Process 1 do { wait (mutex); /{ Critical Section signal (mutex); // remainder section } while (TRUE); do { wait (mutex); // Critical Section signal (mutex); // remainder section } while (TRUE); Usage: Other Synchronization Problems Example: Two concurrent processes P, executing statement S, and P executing statement 52. It is required that Sz be executed only after S, has completed. Pt P2 $1; 8 ‘Assume we definitely want to J have S1 executed before S2. Create a semaphore “x” initialized to 0 Solution: semaphore x = 0; // initialized to 0 Pt. P2 St; wait (: Because X is initialized to X=0, P; will execute signal (x); | | $2; S, only after P, has invoked signal(X), which is i after statement 5, has been executed. usage: resource allocation = Counting semaphore — are used to control access to a finite resource! The semaphore is initialized to the number of resources available. = having integer value can range over an unrestricted domain * Canimplement a counting semaphore S asa binary semaphore * Can solve various synchronization problems ; for example resource allocation Example: Assume we have a resource that has 5 instances. A process that needs that type of resource will need to use one instance. We can allow at most 5 process concurrently using these 5 resource instances. Another process (processes) that want the resource need to block How can we code those processes? Solution: one of the processes creates and initializes a semaphore to 5. semaphore x = 5; // semaphore to access resource walt (x); use one instance Each process has to be of the resource coded in this manner. signal (x); Uses of Semaphore: synchronization Buffer is an array of BUF_SIZE Cells (at most BUF_SIZE items can be put) Producer Consumer dot do { II produce item wait (Full_Cells); put item into buffer remove item from buffer signal (Full_Cells); : } while (TRUE); } while (TRUE); Semaphore Implementation LY = Must guarantee that no two processes can execute the wait ()/ S and signal() onthe same semaphore at the same time = Thus, the implementation becomes the critical section problem where the wait and signal code are placed in the critical section = Single processor— implemented using disabling interrupts = Multiprocessor — compare and swap() or spinlocks = Busy waiting is not completely eliminated = Could now have busy waiting in critical section implementation = But implementation code is short © Little busy waiting if critical section rarely occupied = Note that applications may spend lots of time in critical sections and therefore this is not a good solution Semaphore Implementation with no Busy waiting = With each semaphore there is an associated waiting queue = Each entry in a waiting queue has two data items: = value (of type integer) " pointer to next record in the list = Two operations: = block — place the process invoking the operation on the appropriate waiting queue = wakeup — remove one of processes in the waiting queue and place it in the ready queue = typedef struct{ int value; struct process *list; } semaphore; Implementation with no Busy waiting ( wait(semaphore *S) { so>value--; if (S->value < 0) { add this process to S->list; block(); suspend ooo etoD ; . i signal(semaphore *S) { S->value++; if (S->value <= 0) { remove a process P from S->list; wakeup(P); resume operation Deadlock and Starvation ~~ = Deadlock — There are situations where processes are waiting indefinitely for (an event ) signal() obtained only by one of the waiting processes, a situation called deadlocked. Example: A system consisting of two processes, Py and P,, each accessing semaphores S and Q, initialized to 1. Py P, waits): waitioy: wait); waits) eigen); sigeah(ar; sonal (@) tame (5); = Starvation — indefinite blocking Suppose Py executes wait(S) and then P, waiit(Q). When P) executes wait(@), it must wait until P, executes signal(Q). Similarly, when P, executes wait(S), it must wait until Py executes signal(s). Since these signal() operations cannot be executed, Py and P, are deadlocked. * A process may never be removed from the semaphore queue in which it is suspended Priority Inversion = Priority Inversion — Scheduling problem when lower-priority process holds a lock/ needed by higher-priority process = The situation becomes more complicated if the lower-priority process is preempted in favor of another process with a higher priority. = It occurs only in systems with more than two priorities = Solution: = Only two priorities(insufficient) * Solved via priority-inheritance protocol Classical Problems of ae = Classical problems used to test newly-proposed & synchronization schemes = Bounded-Buffer Problem = Readers and Writers Problem = Dining-Philosophers Problem Bounded-Buffer Problem (The Producer—Consumer Problem) Lt The producer and consumer processes share the following data structures: = n buffers, each can hold one item = Semaphore mutex initialized to the value 1 = Semaphore £ul1 initialized to the value 0 = Semaphore empty initialized to the value n = Pool consists of n buffers, each holding one item. = The mutex semaphore provides mutual exclusion for accesses to the buffer pool. = Empty and full semaphores count the empty and full buffers. Bounded Buffer Problem (Cont.) = The structure of the producer process a do{ /* produce an item in next_produced */ wait(empty); wait(mutex); [* add next produced to the buffer */ signal(mutex); signal(full); } while (true); Bounded Buffer Problem (Cont.) = The structure of the consumer process Do{ wait(full); wait(mutex); /* temove an item from buffer to next_consumed */ signal{mutex); signal(empty); /* consume the item in next consumed */ } while (true); = Note the symmetry between the producer and the consumer. * The producer producing full buffers forthe consumer and the consumer producing empty buffers for the producer. Readers-Writers Problem = A data set is shared among a number of concurrent processes = Readers — only read the data set; they do not perform any updates = Writers —can both read and write = Problem — allow multiple readers to read at the same time = Only one single writer can access the shared data at the same time = This synchronization is called readers—writers problem. = Several variations of how readers and writers are considered — all involve some form of priorities (=) Mes Data Set Oe writer (wn) (os) Readers-Writers Problem Variations Ly) =First readers—writers problem requires that no reader be ! waiting unless a writer has already obtained permission to use the shared object. = Second readers—writers problem requires that once a writer is ready, it perform its write as soon as possible. =A solution to either problem may result in starvation. In the first case, writers may starve; in the second case, readers may starve =Problem is solved on some systems by kernel providing reader-writer locks Readers-Writers Problem = Shared Data = Data set = Integer read_count initialized to 0 = The read_count variable keeps track of Number of readers reading the data at the moment = Semaphore mutex initialized to 1 " Protects the readcount variable, ensure mutual exclusion when the variable read count is updated (multiple readers may try to modify it) = Semaphore rw_mutex initialized to 1 = Protects the data set , The semaphore rw_mutex is common to both reader and writer processes, functioning as a mutual exclusion semaphore for the writers. = (either writer or reader(s) should access data at a time) The structure of a writer process rw_mutex is used by the first or last reader L¥® entering or exiting critical section, but not by readers while other readers are in their critical sections. do { wait (rw.mutex) ; entry critical yale t section section S==-/* writing is performe signal (rw.mutex) ; exit } while (true); section Pelee ; read_count++; if (read_count »_Seniy section wait (rw_mutex) ;- a SM section 1* ‘veading is performed */ va‘e(aieen); — ‘ead_count--; if (read-count == 0) Je section signal (rw_mutex) ; ike (Gru); oa seehOn) } while (true); The structure of a reader process. = If a writer is in the critical section and n readers are waiting, » then one reader is queued on rw_mutex, and n — 1 readers are queued on mutex. =When a writer executes signal(rw_mutex), we may resume the execution of either the waiting readers or a single waiting writer. = The selection is made by the scheduler. Dining-Philosophers Problem = Assume Philosophers spend their lives a alternating thinking and eating * Getting sufficiently hungry, a philosopher tries to pick up 2 chopsticks (one at a time) to eat from bowl = Need both to eat, then release both when done and continues to think. = In the case of 5 philosophers " Shared data = Bowl of rice (data set) = Semaphore chopstick [5] initialized to 1 ba ay resource a process. Dining-Philosophers Problem =Is not areal problem = But lots of real resource allocation problems look like this. If we can solve this problem effectively and efficiently, we can also solve the real problems. = From a satisfactory solution: = We want to have concurrency: two philosophers that are not sitting next to each other on the table should be able to eat concurrently. = We don’t want deadlock: waiting for each other indefinitely. = We don’t want starvation: no philosopher waits forever. Dining-Philosophers Problem Algorithm = The structure of Philosopher i: do ( wait (chopstick[i] ); // wait on left chopstick wait (chopStick[ (i +1) #5] ); // // wait on Right chopstick Ho eat signal (chopstick[i] ); signal (chopstick[ (i + 1) % 5] ); 1) think } while (TRUE) ; = What is the problem with this algorithm? = This solution provides concurrency but may result in deadlock. Dining-Philosophers Problem Algorithm (Cont.) = Deadlock handling a = Allow at most 4 philosophers to be sitting simultaneously at the table. = Allow a philosopher to pick up the forks only if both are available (picking must be done in a critical section. = Use an asymmetric solution -- an odd-numbered philosopher picks up first the left chopstick and then the right chopstick. Even- numbered philosopher picks up first the right chopstick and then the left chopstick. Problems with Semaphores = Incorrect use of semaphore operations: = signal (mutex) .... wait (mutex) = wait (mutex) ... wait (mutex) = Omitting of wait (mutex) or signal (mutex) (or both) = Deadlock and starvation are possible. “A

You might also like