Lecture 8 Code example on csserver in directory /home/hwang/cs470/lecture08 Questions?

advertisement
Lecture 8


Code example on csserver in directory
/home/hwang/cs470/lecture08
Questions?
Friday, January 27
CS 470 Operating Systems - Lecture 8
1
Outline

Peterson's solution

Semaphores
Friday, January 27
CS 470 Operating Systems - Lecture 8
2
Recall: Generalized Process

A process is generalized to:
1. Loop
1.1 Entry Section - code to attempt to gain entry
1.2 Critical Section (CS) - code to be protected
1.3 Exit Section - code to clean up
1.4 Remainder Section (RS) - non-CS code

Protocol is the implementation of Entry and Exit
sections.
Friday, January 27
CS 470 Operating Systems - Lecture 8
3
Recall: Protocol Requirements

A solution to the CS problem must meet three
requirements


Mutual exclusion (ME): when a process is in its
CS, no other process can be in their CS
Progress: if no process is in its CS and there is
some process attempting to get in (i.e., executing
1.1), only waiting processes (i.e., other processes
executing 1.1) can be involved in deciding which
one will be next to enter its CS, and the choice
cannot be postponed indefinitely.
Friday, January 27
CS 470 Operating Systems - Lecture 8
4
Recall: Protocol Requirements


Bounded wait: there must be an upper bound on
the number of times other processes are allowed to
enter their CS's after a process makes a request.
Previous attempts failed the progress
requirement, but other attempts can fail the
other requirements.
Friday, January 27
CS 470 Operating Systems - Lecture 8
5
Peterson's Solution

Peterson's solution combines the ideas from
Attempt 1 and Attempt 2. It uses the flags to
allow immediate access when the other
process is not trying to enter and uses the turn
variable to determine who goes first when both
processes are trying to enter at the same time.
shared bool flag[2] = {false, false} // neither ready
shared int turn = 0 // arbitrary initialization
Friday, January 27
CS 470 Operating Systems - Lecture 8
6
Peterson's Solution
1. Loop
1.1. Set flag[ i ] to true // signal readiness to enter
Set turn to j
// assert other's turn
While flag[ j ] AND turn = j do no-op
// wait while the other is ready and it is
// the other's turn
1.2. CS
1.3. Set flag[ i ] to false // no longer ready
1.4. RS
Friday, January 27
CS 470 Operating Systems - Lecture 8
7
Peterson's Solution

Check the requirements. Need to show that the
criteria always holds. Do so by arguing that the
wrong thing never happens.
Friday, January 27
CS 470 Operating Systems - Lecture 8
8
Peterson's Solution

ME: when Pi is ready (in 1.1), it sets flag[ i ] to true and
turn to j. Consider three cases:



Pj is not ready (in RS), so flag[ j ] is false. Pi enters its CS,
and Pj then cannot enter until Pi leaves since flag[i] is true.
Pj is in its CS, so flag[ j ] is true. Pi sees turn = j and is
prevented from entering its CS until Pj leaves and sets flag[j]
to false.
Pj is ready (in 1.1). Each sees the other's flag is true, but
turn can only be 0 or 1, so only one of the processes can
enter, while the other must wait.
Friday, January 27
CS 470 Operating Systems - Lecture 8
9
Peterson's Solution

Bounded wait: there is only one place a process can
get stuck, the while loop in 1.1 when flag[ j ] is true
AND turn is j. Three cases again when Pi is ready.

Pj is not ready (in RS), so flag[ j ] is false. Pi enters its CS.

Pj is ready, flag[ j ] is true, but turn = i, so P i enters.

Pj is ready, flag[ j ] is true, but turn = j, so P i waits. But when
Pj finishes, it sets flag[ j ] to false. Even if Pj keeps the CPU
and cycles back to 1.1., it sets turn = i, so will wait at 1.1.
Eventually, it will be pre-empted and Pi will see turn = i and
enter. So at most, Pi waits for one turn by Pj.
Friday, January 27
CS 470 Operating Systems - Lecture 8
10
Peterson's Solution

Progress: The same argument also shows progress
since Pj does not affect Pi when it is not ready and
there is a decision in all cases.
Friday, January 27
CS 470 Operating Systems - Lecture 8
11
Peterson's Solution


Peterson's solution primarily is a theoretical one
that shows that CS's can be created using only
load and store operations. Unfortunately, in
many modern architectures, even load and
store are not atomic operations.
Also, it would be very tedious and error prone if
the application programmer had to set up the
solution each time there was a need for a CS.
Friday, January 27
CS 470 Operating Systems - Lecture 8
12
Hardware Synchronization


Hardware techniques for synchronization are
covered in Section 6.4. These include masking
interrupts during the CS, and atomic test-andset or swap instructions.
This is still tedious and error prone. As with all
software engineering problems, we abstract up
a level and come up with higher-level ideas that
are implemented using these techniques. Then
we solve problems using the higher-level
constructs as primitives.
Friday, January 27
CS 470 Operating Systems - Lecture 8
13
Semaphores

One of the first such ideas is the semaphore.
Logically, the semaphore is an integer
initialized to some value with two atomic
operations. Classically, the initial value is 1 and
the operations are defined as:
void wait (semaphore s) {
while (s <= 0)
; // do nothing
s­­;
}
Friday, January 27
void signal (semaphore s)
{
s++;
}
CS 470 Operating Systems - Lecture 8
14
Semaphores

Semaphores can be used to solve any ME
problem. A shared resource has a semaphore
initialized to 1 associated with it (often called a
mutex, short for mutual exclusion) and used in
the following way:
shared semaphore mutex = 1
1. Loop
1.1 wait (mutex)
1.2 CS
1.3 signal(mutex)
1.4 RS
Friday, January 27
CS 470 Operating Systems - Lecture 8
15
Semaphores


If we assume some fairness to the Wait Queue,
then this solution would meet the three criteria
for a correct solution.
In addition, semaphores can be used to
synchronize the order in which processes
execute. This is often done using a semaphore
initialized to 0.
shared semaphore synch = 0
P0: 1. Statement1
P1: 1. wait(synch) // waits for signal
2. signal(synch)
2. Statement2
Friday, January 27
CS 470 Operating Systems - Lecture 8
16
Semaphores


The classical definition of semaphores has only
values of 0 and 1 (also called a binary
semaphore). Most systems generalize to
counting semaphores where initial value is
some n > 0.
Can think of n as the number of processes that
can pass through until a wait call causes an
actual wait. It is also nice if wait( ) always
decrements, then for a semaphore s, when s <
0, |s| is the number of waiting processes.
Friday, January 27
CS 470 Operating Systems - Lecture 8
17
Semaphores

An actual implementation would have the
waiting processes block rather than busy wait.
Such an implementation might look like:
struct Semaphore {
int value;
list<pid_t> l;
};
void wait(Semaphore &s) {
s.value­­;
if (s.value < 0) {
s.l.push_back(PID);
block(PID);
}
}
Friday, January 27
void signal (Semaphore &s) {
s.value++;
if (s.value <= 0) {
PID = s.l.front();
s.l.pop_front();
wake(PID);
}
}
CS 470 Operating Systems - Lecture 8
18
Semaphores


The implementations of signal and wait must be
atomic. Usually use various hardware
techniques.
This does not eliminate busy waiting entirely,
but reduces the size of the CS to just the
bodies of signal and wait.
Friday, January 27
CS 470 Operating Systems - Lecture 8
19
Producer/Consumer Problem



Look at the Producer/Consumer problem again,
which is one of the classical problems in
synchronization.
This problem requires both ME access
synchronization (access to numItems) and
order synchronization (to make sure the
producer does not overwrite and the consumer
does not reread).
Think of the buffer as BUFSIZE slots all initially
empty. Semaphores keep track of empty/full.
Friday, January 27
CS 470 Operating Systems - Lecture 8
20
Producer/Consumer Processes
shared semaphore mutex = 1, // ME to numItems
empty = BUFSIZE, // # of empty
full = 0; // # of full
// Producer
Item nextP;
while (true) {
nextP = MakeItem();
wait(empty);
buffer[in] = nextP;
in=(in+1)%BUFSIZE;
wait(mutex);
numItems++;
signal(mutex);
signal(full);
}
Friday, January 27
// Consumer
Item nextC;
while (true) {
wait(full);
nextC = buffer[out];
out=(out+1)%BUFSIZE;
wait(mutex);
numItems­­;
signal(mutex);
signal(empty);
UseItem(nextC);
}
CS 470 Operating Systems - Lecture 8
21
Producer/Consumer Processes



To see how this works, suppose the producer
starts first without the consumer. The waits on
empty decrement it. The signals on full
increment it. Eventually, empty becomes 0, so
the producer stops. (And full will be BUFSIZE.)
When the consumer starts, the waits on full,
decrement it. The signals on empty increment
it and cause the producer to wake up.
Vice versa, if the consumer starts first. The
wait on full blocks, since it is 0, until the
producer starts.
Friday, January 27
CS 470 Operating Systems - Lecture 8
22
System V Semaphores



An example implementation using System V
(SysV) semaphores is shown in file shm-semprod-cons.cpp
System V semaphore routines are defined in
library <sys/sem.h>.
This is the same program that uses shared
memory with synchronization by semaphores
added. In addition to the mutex, empty, and full
semaphores, there also is a semphore (init) to
make sure the shared memory is initialized.
Friday, January 27
CS 470 Operating Systems - Lecture 8
23
Download