Download CHAPTER 5 – 10/12/01

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Time value of money wikipedia , lookup

Corecursion wikipedia , lookup

Block sort wikipedia , lookup

Drift plus penalty wikipedia , lookup

Scheduling (computing) wikipedia , lookup

Transcript
Concurrency and Mutual Exclusion
CS 350 – 10/12/03
The need for Mutual exclusion in concurrent processing.

Overview – an example:
Concurrent processing implies asynchronism: the outcome of
concurrent processing may be non-deterministic if certain situations
are not prevented:

Example of a “Race Condition”
This is an example of a “print spooler”. It is taken from taken from
“Modern Operating Systems”, by Andrew Tanenbaum, pp. 33-34. It
illustrates an apparently mundane situation, and an obvious but naïve
approach which will deliver erroneously results.
o A process wanting to print a file and enters the file name in a print
queue to await printing.
o A print “daemon” periodically checks the queue for files to be
printed and if so it chooses one, removes it from the queue and
prints it.
o Assume the queue is a infinitely large array whose slots are
indexed: 0,1,2,…
o There are two shared variables:
out - points to next file in queue to be printed
in – pints to the next free slot in the queue
o Assume slots 0 through 3 are empty (files already printed) and
slots 4 though 6 are full (they have names of files yet to be printed)
– next free slot is 7.
o Assume processes A and B both want to print a file and the
following sequence takes place:
CS350
Fall 2003
Principles of concurrency
page 1
Process A reads “in” and stores the value 7 in a local
variable next_free_slot.
A context switch now takes place (time slice) and process B
switches into active execution. Process B also reads “in” and also
gets a 7 so it stores the name of the file in slot 7 and updates “in”
to be an 8 – it then goes off and does something else.
Process A now becomes active again and using
next_free_slot, finds a 7 there and writes its file name in slot 7,
erasing the name that process B just put there. It then puts an 8 in
“in”
-
CS350
Fall 2003
process B never gets its file printed.
Principles of concurrency
page 2
Mutual Exclusion: Software Approaches
 We consider two “attempts’ to solve the mutual exclusion problem:

All solutions suffer from busy waiting, and will indefinitely block the other
process if one process crashes in the critical section.
 First attempt:
int turn = 0; // initial value for turn
/* process 0 */
/* processes 1 */
while(turn != 0) no-op;
while(turn != 1) no-op;
/* critical section*/
/* critical section*/
turn = 1;
turn = 0;
}
}
Strict alternation between two processes via use of shared variable “turn”
Mutual exclusion achieved
Two problems:
Performance determined by least active (slowest) process
If one processes crashes outside of the critical section, the other will wait
forever for the CS.
 Second attempt:
/* process 0 */
/* processes 1 */
flag[0] = true;
flag[1] = true;
while(flag[1]) no-op;
while(flag[0]) no-op;
/* critical section*/
/* critical section*/
flag[0] = false;
flag[1] = false;
}
}
Crash outside CS will not cause indefinitely block other process
Mutual exclusion achieved
Problem: Deadlock caused by race to set flag[i]
CS350
Fall 2003
Principles of concurrency
page 3
Correct Software Solutions:
 Dekker’s Algorithm:
Combines using the “turn” variable and flag[] variable.
Avoids mutual courtesy
“turn” guarantees mutual exclusion.
“flag[]” breaks strict alternation problem – if your flag is set to 1 and other
flag is 0, then enter no matter what turn is.
In the event that a race causes flag[0] = flag[1] = 1, then there is no deadlock
to get to the CS because turn will allow one of the processes to enter.
flag[i] is an indication of “intent” to enter.
See flow chart below for details.
CS350
Fall 2003
Principles of concurrency
page 4
Initial values: flag[0] = flag[1] = 0
CS350
Fall 2003
Principles of concurrency
page 5
 Peterson’s Algorithm:
Interpret flag[i] as indicting that process i is “interested” in entering the CS.
Turn resolves the simultaneity conflicts as with Dekkers
Does the same as Dekkers, but is simpler.
See flow chart below for details:
CS350
Fall 2003
Principles of concurrency
page 6
Initial values: flag[0] = flag[1] = 0
 This is as good as it gets with software solutions – the nemesis of all
software solutions is:
Busy waiting
Let’s see what hardware and the OS has to offer.
CS350
Fall 2003
Principles of concurrency
page 7
Mutual exclusion: hardware support
 Interrupt disabling:
while(1) {
/* disable interrupts */;
/* critical section
*/;
/* enable interrupts */;
/* remainder section */;
}
Mutual exclusion guaranteed.
High cost: execution inefficient I uniprocessor cannot interleave processes.
Will not work a multiprocessing architecture – physical parallelism –
synchronizing interrupts across processors a problem.
 “test-and-set” instruction
See Stallings page 214 and fig. 5.5a
In all cases i == 1 means CS is locked, “the door is locked”
boolean testset (int i) { //”alternate definition” flow chart, next page
if (i == 0) { i = 1; return false; }
else {return true}
}
Note: if i == 1 (locked), i stays 1 and returned 1
if i == 0, change i to 1 and return 0 and enter CS
Another way of coding the same function (“1st definition” flow chart, next
page:
boolean testset (int i) {
if ( i = = 0) {i = 1; return true;}
else return (complement if i)
}
CS350
Fall 2003
Principles of concurrency
page 8
Flow Chart:
For “alternate” definition for test&set(i)
if (i == 0) {i = 1; return 0;}
if (i == 1) return 1;
// or more simply:
j = i;
i = 1;
return j;
CS350
Fall 2003
Principles of concurrency
page 9
“exchange” or “swap” instruction
void exchange (int register, int memory) {
int temp;
temp = memory;
memory = register;
registger = temp;
}
Flow chart:
CS350
Fall 2003
Principles of concurrency
page 10
Semaphores
A synchronization mechanism provided by the operating system based on a
signaling and blocking scheme.
Will enforce mutual exclusion and also regulate the operation of processes
based on the values of certain global numerical variables (counting variables).
Some properties of semaphores: A signal is transmitted via semaphore s by
executing a primitive called “signal(s)”. To receive a signal via semaphore s, a
process executes the primitive “wait(s)”. A wait call could cause a process
block depending on the value of a semaphore s. A signal could release a
blocked process depending on the value of the semaphore upon which it is
waiting.
Definition of a semaphore:
A semaphore, s, is a data structure consisting of an integer and a queue.
A semaphore, s, integer may be initialized to a nonnegative value.
The wait(s) operation decrements the value of the semaphore integer.
If the semaphore value becomes negative, the process P calling the wait is
blocked and goes on the queue of the semaphore. P is said to be waiting on the
semaphore s.
The signal operation increments the semaphore integer; if the value is not
positive, then a process blocked in the queue of this semaphore is released.
Why it works: the signal(s) and wait(s) operations are atomic. These
operations cannot be interrupted and are considered indivisible.
Below are the specifications (description) of wait and signal using C
pseudocode.
CS350
Fall 2003
Principles of concurrency
page 11
Definition of a general Semaphore Primitives from Stallings Figure 5.6.
struct semaphore {
int count;
// value of semaphore
queueType queue; //list of processes waiting on this semaphore
} s; // s is the declared semaphore variable
void wait(s) {
s.count--;
//decrement semaphore value first
if (s.count < 0) { // test for negative
place this process in s.queue;
block this process;
}
}
void signal(s) {
s.count++;
//decrement semaphore value first
if (s.count 0) { // test for non-positive
remove a process P from s.queue;
place process P on ready list;
}
}
How General Semaphore are Used:
There are two common types of general semaphores: mutual exclusion
semaphores controlling of a critical section, and counting semaphores
controlling the action of processes that access a limited renewal resource such
as reading and writing a finite memory buffer.
Mutual Exclusion Semaphores
Such semaphores are commonly referred to as “mutex” semaphores. The
mutex can be a general semaphore initialized to a one (1). When the value is a
1, the “door” is open and any process can enter its critical section. The first
process to enter will do a wait(mutex) and cause the mutex to decrement to a 0
before entering. The next process trying to enter will also do a wait(mutex)
causing the mutex to decrement to a “–1” and will block in the queue. Any
other processes trying to enter will also block on wait after further
decrementing the semaphore. All such blocked processes are added to the
mutex queue. When the process in the critical section exits, it will do a
CS350
Fall 2003
Principles of concurrency
page 12
signal(mutex) and this will free up one of the blocked processes in the queue
and also increment the semaphore by one. If the semaphore is negative, is
absolute value is the number of processes waiting on this semaphore.
Counting Semaphores
This case is best described by the bounded buffer example. Assume that the
buffer is a circular array of, say, 10 storage “cells”. Let a semaphore called
“unused” be the count of the number of unused slots in the buffer, and another
semaphore called “used” be the count of the number of used slots in the buffer.
unused is initialized to 10 and used is initialized to 0. Each time the an item
is produced in the buffer “unused” is decremented by 1 and “used” is
incremented by 1. Each time an item is consumed from the buffer, “used” is
decremented by one and “unused” is incremented by 1. Decrementing is done
by doing a wait on the semaphore, and incrementing is done by incrementing
the semaphore. If the waits are done before accessing the buffer (critical
section), then the producer (waiting on unused) will block if unused is zero
(buffer full), and consumer (waiting on used) will block if used is zero (buffer
empty). signals are done after accessing the buffer and when the producer
signals on used, a blocked consumer will be released and the used count will be
incremented. The consumer will signal on unused after accessing the buffer
(consuming) and a blocked producer will be released and the unused count will
be incremented.
Unless the buffer is full or empty, it is possible for both the producer and
consumer to be in the critical section at the same time. Mutual exclusion can
also be achieved by using a mutual exclusion semaphore as above. See the
bounded buffer example below.
CS350
Fall 2003
Principles of concurrency
page 13
Binary Semaphores
Binary Semaphores have a value which can only vary between 0 and 1. If the
value is 1 the wait will not block. If the value is 0, the wait will block and the
blocked process will be queued on the semaphore. The signal opertation will
set the value to 1 if the queue is empty, otherwise it will leave it at 0. The
implementation is given below:
struct binary_semaphore {
enum (zero, one) value;
queueType queue;
}
void waitB(binary_semaphore s) {
if (s.value ==1) s.value = 0;
// Lock the “door” & let process enter CS
else {
place this process in s.queue;
block this process;
}
}
// no indication of size of queue ... compare to general semaphore
// wait unconditionally leaves b-semaphore value at 0
void signalB(binary_semaphore s) {
if (s.queue is empty) s.value = 1;
//s.value==0 is necessary but not sufficient
// condition for empty queue
else {
remove a process from s.queue;
place this process in the ready queue;
}
}
CS350
// if only 1 proc in queue, leave value at 0, since “moved” proc will go to CS
Fall 2003
Principles of concurrency
page 14
An example on how mutual exclusion is achieved with a semaphore:
// pseudocode for process i of n processes
semaphore mutex = 1;
void mutual_exclusion(i) {
while (1) {
wait(mutex);
< critical section >;
signal(mutex);
}
< remainder section>;
}
Example of three processes using general or “counting” semaphores and a
binary semaphore based on fig 5.10 Stallings (for binary Semaphores):
CS350
Fall 2003
Principles of concurrency
page 15
The producer/consumer problem:
// boundedbuffer;
int SIZE
// size of buffer
semaphore mutex = 1; // this is only pseudocode, initializing
semaphore used = 0; // a semaphore is actually a system call,
semaphore unused = SIZE;
//Note:
// used means the number of occupied slots in buffer
// unused means the number of empty slots in buffer
void producer() {
while (1) {
produce-item(); // non critical (remainder) section
wait(unused);
wait(mutex);
append-item(); // critical section
signal(mutex);
signal(used);
}
}
void consumer(); {
while (1) {
wait(used);
wait(mutex);
take();
// critical section
signal(mutex);
signal(unused);
consume();
// non critical (remainder) section
}
}
CS350
Fall 2003
Principles of concurrency
page 16
A bounded buffer scenario for a buffer of size 3:
In this example mutual exclusion will be ignored, and only the counting
semaphores will be illustrated (producer starts first).
producer
event
initial value
wait(unused)
signal(used
wait(unused)
signal(used)
wait(unused)
signal(used)
wait(unused)
blocked
in ready queue
in ready queue
in ready queue
signal(used)
wait(unused)
signal(used)
in ready queue
in ready queue
in ready queue
in ready queue
in ready queue
in ready queue
in ready queue
wait(unused)
CS350
Fall 2003
unused
semaphore
3
2
2
1
1
0
0
-1
-1
0
0
1
1
0
0
0
1
1
2
2
3
3
2
used
semaphore
0
0
1
1
2
2
3
3
2
2
1
1
2
2
3
2
2
1
1
0
0
-1
0
Principles of concurrency
consumer
comments
event
in ready queue, queue empty
in ready queue
in ready queue
in ready queue
in ready queue
in ready queue
in ready queue, queue full
in ready queue, producer blocks
wait(used)
signal(unused)
wait(used)
signal(unused)
in ready queue
in ready queue
in ready queue, queue full
wait(used)
signal(unused)
wait(used)
signal(unused)
wait(used)
signal(unused), queue empty
wait(used), consumer blocks
in ready queue
page 17