chapter 6.2: process synchronization part 2. 6.2 silberschatz, galvin and gagne ©2005 operating...

23
Chapter 6.2: Process Chapter 6.2: Process Synchronization Synchronization Part 2 Part 2

Post on 21-Dec-2015

230 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

Chapter 6.2: Process SynchronizationChapter 6.2: Process SynchronizationPart 2Part 2

Page 2: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.2 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Module 6: Process SynchronizationModule 6: Process Synchronization

Lecture 6.1 Background The Critical-Section Problem Peterson’s Solution Synchronization Hardware

Lecture 6.2: Semaphores Classic Problems of Synchronization

Lecture 6.3: Monitors Synchronization Examples Atomic Transactions

Page 3: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.3 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Test And Set Instruction Test And Set Instruction Definition: Here’s the Test and Set Instruction… It is executed atomically. Consider the code:

boolean TestAndSet (boolean *target)

{

boolean rv = *target; // rv set to the value of target

*target = TRUE; // target set to TRUE

return rv: // value passed to TEstAndSet() returned

} // via rv.

We will see how this is used (implemented) on the next slide…

But you can see that a pointer value (dereferenced) ‘to something’ is passed to TestAndSet; a boolean variable rv is set to the pointer’s value. The dereferenced value of the actual parameter is set to TRUE, and we return this boolean value of rv which will be whatever was passed to TestAndSet().

Note: TestAndSet() returns a boolean value passed to it, but it sets the global

variable to TRUE.

Page 4: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.4 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Mutual Exclusion via TestAndSet() But we implement the mutual exclusion shown by TestAndSet() by using a

global boolean lock as the parameter and it is initially set to false. Consider:

do {

while (TestAndSetLock (&lock))

; // do nothing

// if lock is false (see above) , value of predicate is false (but remember // lock itself was set to TRUE), and we drop into critical section;

// if TestAndSet() returns ‘true’ (which it would if another process

// was already executing its critical section) , do nothing; Spin…

// critical section

lock = FALSE; // reset global variable as part of this ‘instruction.’

// remainder section

} while (TRUE);

Remember, TestAndSet() is atomic, the routine above includes TestAndSet() but the

overall execution is NOT atomic (only the TestAndSet() part).

Let’s look at another hardware instruction that uses two variables: a lock and a key.

Page 5: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.5 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Swap() – can provide mutual exclusion too…

This hardware approach deals with two words and is also executed atomically.For machines that support a swap() instruction (remember, this is a hardware instruction), we can provide mutual exclusion as seen in the code below: // lock is a global Boolean variable initially declared to be FALSE. // Also, each process has a local Boolean variable key. do{ key = TRUE;

while (key == TRUE) // recall: lock is global and declared to be FALSE. Swap (&lock, &key); (Invoke swap() - in book and quite standard…)

// this swap() will set lock to TRUE and key to FALSE.// // So ‘if’ some other process could tries this code,

.. Key ‘starts off’ in this other process to TRUE

.. But lock is TRUE from ‘this’ process execution...// the Swap (note lock is global) both would be False and// So both key and lock = FALSE in this new, trying code, and // there is no entry to the critical section…// Note: since SWAP is atomic, if this original routine ‘has’

// lock; no other routine has it and cannot thus change it…// the while loop will essentially be a busy waiting for the other// process to finish up set lock to False.

// critical section lock = FALSE // can see this resets the lock to FALSE // remainder section

} while (TRUE);

Page 6: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.6 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

How about Bounded-Wait Time?

Both of these algorithms satisfy the mutual exclusion need but neither satisfies the bounded waiting time need, which says that the requesting code will be executed after a finite wait time.

Note the ‘bounded waiting time’ need refers to the fact that there needs to be some kind of limit on the number of times that other processes are allowed to enter their critical sections after a different process has made a request to enter its critical section. Equivalently, a process needing to enter its critical section cannot

be denied forever. This algorithm is presented as the Bounded-waiting Mutual

Exclusion with TestAndSet() implemented a bit differently. (Figure 6.8)

Recommend you plow through this to understand the code…

Page 7: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.7 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Finishing Up Hardware Instructions

So the last algorithm covers the bounded wait time and mutual exclusion.

The key is how one implements these hardware instructions. Implementing the TestAndSet() instructions on multiple processors

is not easy. Your book does not cover the details, but note that because the

instructions are atomic (and this is the key) the real implementation of these instructions requires (likely) a carefully architected number of microinstructions, assuming the control unit of the CPU is micro-coded….

Regardless, the instruction is burned into the hardware – silicon.

Let’s now turn our attention to how Critical Sections can be handled in software.

We will introduce the concept of a semaphore.

Page 8: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.8 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

SemaphoreSemaphore A semaphore is merely an integer variable that can be binary (have values 0 or 1) or be a

counting semaphore (have integer values – generally positive, but not always). Bear in mind that semaphores are used to control access to shared variables and shared

resources and to prevent horrible unpredictable problems that might occur when more than one process accesses the shared resource before another process is finished with it.

As in the case of atomic hardware instructions (TestAndSet() and Swap() ), the integer variable, the semaphore, is accessed and modified by atomic operations, normally called wait() and signal(). It is important to note that these operations used to be called P (to test) and V (to increment) semaphores.

Also, please note that semaphores themselves are not atomic but employ a critical section that is atomic by its use of TestAndSet or similar hardware instruction.

Here’s how they work on the semaphore, S: Note instructions are indivisible!!!!

wait (S) Here’s the wait routine.

{

while S <= 0

; // no-op

S--;

}

signal (S)

{

S++;

}

Note that there are actually two ‘instructions:’ a test to see if S <= 0 and a decrement to S.

One instruction – as found in signal() to increment the semaphore, is sufficient because the process ‘has’ the resource protected by the semaphore accessed in its critical section. So, this process is merely releasing it by incrementing the semaphore.

Page 9: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.9 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Semaphore as General Synchronization ToolSemaphore as General Synchronization Tool As defined and because the instructions are indivisible, when one process

modifies a semaphore value no other process can simultaneously modify that same value.

Please note that, as mentioned, the wait involves potentially two instructions treated as one: the test of S and its decrement. These cannot be interrupted.

In contrast to a binary semaphore, or Counting semaphore, will be an integer whose value that can range over an unrestricted domain

Binary semaphore – integer value can (typically) range only between 0 and 1. This may be simpler to implement but used to control access to different resources.

Binary semaphores are also called mutex locks on some systems.

Counting semaphore: One may also implement a counting semaphore S as a binary semaphore

This approach also provides mutual exclusion and can be used as follows: Semaphore S; // initialized to 1 wait (S); // if this process gets ‘through’ the wait(), it gains access to its critical section.

Critical Section

signal (S); Here the counting semaphore may not necessarily only have values of 0 or 1 even

though it is using wait() and signal(), which decrement and increment semaphore, S.

Page 10: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.10 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Counting Semaphores Counting Semaphores

Counting semaphores are often used to control access to a number of similar resources, such as disks, tape drives, CDs, etc. We may have eight disks available for allocation to processes and a counting

semaphore keeps track of the number remaining to allocate (if any).

A semaphore is typically initialized to the number of resources available Wait() decrements the semaphore; Signal() increments the semaphore.

Presumably, if the value of the semaphore is ‘acceptable,’ the process enters its critical section.

If count goes to 0, then no more of these resources are available and the requesting process will likely block until the semaphore becomes greater than 0.

Note: TestAndSet() instructions usually implement a ‘busy waiting,’ while semaphores normally ‘block’ the requesting process.

Page 11: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.11 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Semaphore ImplementationSemaphore Implementation The main problem in using semaphores arises in implementation. While a process is in its critical section, any other process that tries to enter its critical section

must loop continuously in the entry code. We call this ‘busy waiting’ and can be a real issue. The notion of busy waiting is

fundamental to the notion of accessing shared resources. We readily see this via the wait() instruction – in particular:

wait (S) {

while S <= 0

; // do nothing. S is not decremented until S > 0 Process does not // enter its critical section

// rest of wait(S) ….

We call such a situation Busy Waiting, and it wastes valuable CPU time, as CPU is not computing anything while it ‘does nothing.’

This type of semaphore is called a spinlock. (spin lock = mutual exclusion mechanism in which a process executes in an infinite loop waiting for the value of a lock variable to indicate availability)

Busy Waiting is not always bad (and is used in some cases – ahead…): No context switching When locks are expected to be of short duration, this may not be too bad either. Busy waiting not a big problem if critical section rarely occupied

Note that applications may conceivably spend lots of time in critical sections and therefore this is not a good phenomenon in all situations across the board.

Here’s the busy waiting

Page 12: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.12 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Semaphore Implementation with no Busy Waiting Semaphore Implementation with no Busy Waiting So let’s look at a different implementation of wait() and signal() to

address the busy waiting problem.

When a process executes the wait() and might ordinarily loop (busy waiting) and lose valuable CPU time, let the process block itself. Many implementations of semaphores take this approach!

This system call, block(), will place the process into a wait queue for the semaphore; state of process becomes wait, of course, but the CPU can be switched to another process for execution.

When a process then executes a signal() operation, the ‘blocked process’ awaiting semaphore S can be ‘awakened’ and its state changed from wait to ready. This process may now be rescheduled for the CPU.

Or the CPU may be dispatched to this process right away - depending upon the CPU scheduling algorithm and any priorities it might provide to a queue for semaphores.

Page 13: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.13 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Semaphore Implementation with no Busy WaitingSemaphore Implementation with no Busy Waiting Let’s look at some code: The data structure constituting the semaphore is defined as a structure.

typedef struct {

int value;

struct process *list;

} semaphore; Can see the semaphore has an integer value and a list of process names. When a process issues a wait, the integer value is decremented. (next slide) If a process must wait for the semaphore (value < 0) , its name is added to the

list (above), and a block() is issued. Process is then moved to wait state; CPU dispatched elsewhere…

When signal is executed, value is incremented, and if value is <= 0, then a process is removed from the list of processes and the process is ‘awakened.’

Please note that there is no guarantee, as mentioned, that this newly awakened process is ‘thee’ process awaiting the semaphore most recently. This depends on the CPU scheduling algorithm(s).

Main point here is that busy waiting processes become blocked using this scenario, CPU remains executing, and we essentially have a counting semaphore that contains a list of waiting processes seeking to execute their critical sections.

Page 14: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.14 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Semaphore Implementation with no Busy waitingSemaphore Implementation with no Busy waiting (Cont.)(Cont.) Here’s the code: Implementation of wait:

wait (S){

value--;

if (value < 0) { // if true, means value was at most 0

add this process to waiting queue

block(); }

}

Implementation of signal:

signal (S){

value++;

if (value <= 0) { // true value was not >= 0

remove a process P from the waiting queue

wakeup(P); }

}

These two operations are provided by the operating system as basic system calls! This is extremely important. Programs and programmers issue system calls!

Note that this implementation allows negative values. This is true due to switching the order of decrementing and testing in the wait() implementation.

Page 15: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.15 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Semaphore Implementation with no Busy waitingSemaphore Implementation with no Busy waiting (Cont.)(Cont.) Here’s the code using pointer references with a bit more rigor:: Implementation of wait:

wait (semaphore *S) {

S -> value--;

if (S->value < 0) {

add this process to S -> list;

block();

}

} // end wait()

Implementation of signal:

signal (semaphore *S){

S ->value++;

if (S->value <= 0) {

remove a process P from the S->list;

wakeup(P); }

} // end signal()

Again, no two processes can execute a wait() and a semaphore() on the same semaphore at the same time. These are indivisible instructions.

Page 16: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.16 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

More – The System Calls and the Queue

block() suspends the process that invokes it. wakeup(P) resumes the execution of a blocked process. These take place as system calls. They are provided to us by your

friendly operating system.

The list of process may be implemented by a link field in the PCBs of these processes. The semaphore contains an integer value and a pointer to the list of PCBs. A simple linked list does the job here.

A big plus in this implementation is that by using a FIFO queue we ensure that wait time is bounded. A process will get the CPU in a finite amount of time. Thus the bounded waiting problem is addressed. (no good process left unexecuted…Ha)

Head and tail pointers to the queue of PCBs completes the strategy. PCBs can be readily added to (inserted to the tail) and removed from

(removed from the head) the queue.

Page 17: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.17 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Single and Multiple Processor Issues

No problems with single processors when using wait() and signal() atomically.

In the single processor environment, interrupts are simply inhibited during the time wait() and signal() operations are executing.

Ah, but multiple processors? A different set of issues. Interrupts disabled on each processor would appear to be a must. But disabling interrupts does take time and disabling interrupts on

a number of processors will definitely degrade overall performance and throughput.

So, SMP systems provides alternative locking techniques, like spinlocks, to guarantee wait() and signal() are performed without interruption (atomically).

Previews of coming distractions…

Page 18: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.18 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Deadlock and StarvationDeadlock and Starvation

Deadlock – two or more processes are waiting indefinitely for an event that can be caused by only one of the waiting processes

We will wait for Chapter 7 to discuss Deadlock. Essentially one process, P1, is holding one resource, R1,

awaiting another R2 in order to continue, while another process, P2, is holding R2 but is waiting on R1 to continue.

Neither process can continue. We call these deadlocked processes. For multiple instances of resources, a number of processes

can be deadlocked, and this can bring the system to a grinding halt, as this number can grow over time…

Deadlock normally refers to acquisition and control of shared resources and is considered a huge problem.

But we shall see how deadlock is addressed in Chapter 7.

Page 19: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.19 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Classical Problems of SynchronizationClassical Problems of Synchronization

We will discuss all three of these very important topics.

Bounded-Buffer Problem

Readers and Writers Problem

Dining-Philosophers Problem

Page 20: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.20 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Bounded-Buffer ProblemBounded-Buffer Problem

Idea here is to control a number of resources via a buffer pool.

Each element in the buffer contains one item.

Buffer is of fixed size.

We need a mutex semaphore (initialized to 1) and two more semaphores empty and full, where empty is initially set to the maximum items available in pool, n, and full is set to 0.

Best used in a producer-consumer relationship where a number of resources and controlled in the buffer.

Code is straightforward. The producer moves an item into a buffer

The consumer removes an item from the buffer.

We are only showing the concept here.

Page 21: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.21 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Bounded Buffer Problem (Cont.)Bounded Buffer Problem (Cont.)

The structure of the producer process

do {

// produce an item

wait (empty); // can see process may have to wait if empty is true.

wait (mutex); // After wait(), however, we ensure mutual exclusion// to gain access to the buffer.

// add the item to the buffer

signal (mutex); // releases the lock

signal (full); // what do you think this will do?

} while (true);

Page 22: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

6.22 Silberschatz, Galvin and Gagne ©2005Operating System Concepts

Bounded Buffer Problem (Cont.)Bounded Buffer Problem (Cont.)

The structure of the consumer process

do {

wait (full); // may have to wait if full is zero

wait (mutex); // provides for mutual exclusion

// remove an item from buffer

signal (mutex); // releases its hold on mutual exclusivity

signal (empty); // what do you think this does?

// consume the removed item

} while (true);

Page 23: Chapter 6.2: Process Synchronization Part 2. 6.2 Silberschatz, Galvin and Gagne ©2005 Operating System Concepts Module 6: Process Synchronization Lecture

End of Chapter 6End of Chapter 6Part 2Part 2