Cosc 4740 Chapter 6, Part 2 Process Synchronization.

Post on 17-Dec-2015

222 views 1 download

transcript

Cosc 4740

Chapter 6, Part 2

Process Synchronization

Busy loop/waiting problem

• Wastes CPU time

• If scheduler takes into account priority, then low priority process (becomes CPU bound), while other may be high priority– If low priority gets the lock, while the high

priority process will have to wait longer while low priority process finishes/release lock.

Busy loop fix

• Can use sleep() and wakeup()• sleep() blocks the process

– it won’t be scheduled

• wakeup() moves the sleeping process to the ready list.– If it is blocked for other reasons don’t wake it.– The wakeup() call is issued by another process. The

sleeper can not wakeup itself. • Can be setup for a timed event, ie sleep for 1 second, but O/S

“Wakes” the process after 1 second.

Producer/Consumer or Bounded buffer problem

• 2 processes communication by sharing a fix sized buffer. – (limited way): The producer puts stuff in and the

consumer takes stuff out.

– Producer buffer consumer

• It can not just put it in or remove it randomly. The buffer can be full or empty. Must be a balance between producer and consumer.

First attempt

Buffer: //classAn arraynext: //index variable – next input location // –producer interested in thislast: // next output location //– consumer interested in thisN: //size-fixed for buffer. Producer() while (true) {

produce (item) put_info( buffer[next], item) next = (next+1) mod N }

• This is a simple minded version. Assumes never full, never empty, no problems

Consumer()

while (true) {

get_item(buffer[last], item)

last = (last +1) mod n

consume(item)

}

• Problems:

• what if buffer is full or empty?– If full – don’t put_info– if empty – don’t get_item.

• One solution is a busy waiting loop while empty or full. 

• OR use sleep() and wakeup()

• int count = 0 // global –shared between the processes = # items in buffer array

producer ()while (true) {

produce (item)if (count == N) sleep();enter_item(item)count++

if (count == 1) wakeup(consumer)

}

Consumer ()

while (true) {

if (count == 0) sleep()

remove_item(item)

count --;

if (count == N-1)

wakeup(producer)

consume(item)

}

• problems:– Consumer codes running reads count and it is zero.

Before it goes to sleep, the process is interrupted.– The producer process starts running

• puts item in buffer, count = 1 and wakeup consumer. • Which would do nothing, since it was interrupted and already

on the ready-queue.

– Consumer switched onto the CPU and then goes to sleep.

– Producer runs until buffer is full and it too goes to sleep.

The Fix

• In 1965 Dykstra invented semaphores to solve these problems.

Quick Review

• tsl instruction ()– it’s an atomic instruction

– allows programmer to set a lock without interruption

• problem: busy/waiting wastes CPU• solution: sleep & wakeup• problem: w/ sleep & wakeup – no count of how

many proc are sleeping so could put all to sleep

Semaphore• Synchronization tool that does not require

busy waiting • Semaphore S – integer variable

– contains non-neg integers (>= 0)– Provides a way to count the number of

sleep/wakeups invoked

• Two standard operations modify S: wait() and signal()– Originally called P() and V()

Semaphore as General Synchronization Tool

• Counting semaphore– integer value can range over an unrestricted domain

• Binary semaphore– integer value can range only between 0 and 1; can be simpler to

implement– Also known as mutex locks

• Can implement a counting semaphore S as a binary semaphore• Provides mutual exclusion

Semaphore S; // initialized to 1wait (S); Critical Sectionsignal (S);

Semaphore Implementation

• Must guarantee that no two processes can execute wait() and signal() on the same semaphore at the same time– Wait() and Signal() are also critical sections

for the variables S.

– Note that applications may spend lots of time in critical sections and therefore busy waiting is not a good solution.

Semaphore Implementation (2)

• 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.

Semaphore Implementation (3)

• Implementation of wait:

wait (S){

S->value--;

if (value < 0) { //add this process to //waiting queue for S block(); }

}

• Implementation of signal:

Signal (S){

value++;

if (value <= 0) { //remove a process P //from the waiting queue //for S wakeup(P); } }

Deadlock and Starvation

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

• Let S and Q be two semaphores initialized to 1P0 P1

wait (S); wait (Q); wait (Q); wait (S);

. .

. .

. . signal (S); signal (Q); signal (Q); signal (S);

• Starvation – indefinite blocking. A process may never be removed from the semaphore queue in which it is suspended.

• Priority Inversion – Scheduling problem when lower-priority process holds a lock needed by higher-priority process– Solved via priority-inheritance protocol

Classical Problems

• Solutions using Semaphores– Bounded-Buffer Problem

• Producer-Consumer Problem

– Dining-Philosophers Problem– Readers and Writers Problem

Bounded-Buffer Problem

• N buffers, each can hold one item

• Semaphore mutex initialized to the value 1

• Semaphore full initialized to the value 0

• Semaphore empty initialized to the value N.

Bounded Buffer Problem (Cont.)

• The structure of the producer process

while (true) {// produce an itemproduce(item);

wait (empty); //check if buffer is full, if yes, block wait (mutex); //enter CR to add item.

// add the item to the buffer

signal (mutex); //leave CR system.signal (full); //increment full

}

Bounded Buffer Problem (2)

• The structure of the consumer process

while (true) {wait (full); //check to see if buffer is “full”

//start w/full =0, so it blocks if there is nothing yet. wait (mutex); //Enter CR // remove an item from buffer

signal (mutex); //leave CRsignal (empty); //increment empty// consume the removed item

}

Bounded Buffer Problem (3)

• The producer block should be removed when at least 1 item is removed

• The consumer must tell the producer by a signal(s) operation.

• Recall that semaphores keep a count of wait() & signal() calls and are atomic functions.

Dining-Philosophers Problem

• 5 philosophers • 5 forks• Each philosopher

– (1) eat– (2) thinks

• Catch: If a philosopher wants to each it needs two forks

philosopher(i)

while(1) {

think()

pick up 2 forks

eat()

put down forks

}

• Problem: – To eat, a philosopher

needs 2 forks

– 2 neighbors can not eat at the same time.

– can not pick up more than 1 fork at a time.

– So 1 philosopher might starve!

With No locking.

philosopher(i) left = I; right = (I+1) mod 5;

while(1) {think()pick_fork[right] =1 pick_fork [left ] =1eat ()put_fork [right]=0put_fork [ left] =0

}

With Semaphores

semaphore S[5] = {1,1,1,1,1} // init to 1

while (1) {

think();

Wait(S[left]);

Wait(S[right]);

eat ()

Signal(s[left]);

Signal(s[right]);

}

• Now when a fork is not avail, the philosopher will block.

• Problem: – If all philosophers pick up left fork 1st, then no

right forks are available.

• We need to be able to pick up both forks or none, so we need a semaphore to do this: make picking up forks atomic.

Philosphers(i) { take_forks(i) {Wait(mutex);

while(1) { Wait(S[left]);think(); Wait(S[right]);take_forks(i); Signal(mutex);eat(); }put_forks(i);

} }put_forks(i) {

Wait(mutex);Signal(S[left]);Signal(S(right]); So what problems arise?Signal(mutex);

}

• So what problems arise?

– If philosophers is blocked on a right or left fork, he will be deadlocked, because no other process can put_forks, since it is in it’s critical section!

• There are other methods that can fix this problem.

Readers/writers problem

Two types of processes

1. readers – reads information from the database

2. writers – write to the database

 

• And we want concurrent access to the database.

• problem 1– 2 writers try to modify the db at same time.– constraint: At must 1 writer should access the database

at any time

• problem 2 – a reader is attempting to read an entry while it is in the

process of being modified and could read an intermediate value (or wrong value).

– Constraint: A writer and reader cannot access the database at the same time.

try #1

semaphore db = 1;writer () {

P(db); // protect db from other writers and readersmodify database V(db);

}readers ()

P(db);read databaseV(db);

}• no problem with constraints but it excludes other readers

try #2

• we want to allow multiple readers, but no readers w/ writers

v = 0;db =1;writer() reader()

P(db) if (v==1)v = 1; read (database)modify(database)v = 0;V(db)

• Problem: v is not protected. When you access v, it should be w/in a semaphore

• Why?

• Also readers can already be in the database, when the writer enters. Writer is not blocked by readers.

• P(db) in reader, will also block readers too.

• We want a P(db) block on writers put on the by the 1st reader only and removed by last reader!

Try #3

semaphore mutexint rc =0; //reader count reader()

P(mutex) // Protect rcif (rc ==0) { P(db); }rc ++;V(mutex);

read database; 

P(mutex)rc –if (rc ==0) { V(db); }V(mutex);

writer()

P(db);

modify database

V(db);

• This solution allows multi readers– no writer/reader combo, but writer will starve

• may never get a chance if readers keep arriving. Writer unblocked only when all readers done.

• So what is a fair solution?– We need to alternate between writers and readers.– You must maintain a count of # of readers reading and

you must maintain a flag when writer arrives.

– Can be done with higher level primitives to make it easier.

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

higher level primitives

• There are two primitives that well look at:

• Conditional Critical Region (CCR)

• Monitor

Conditional Critical Region

• Explicitly designates a section of the program as critical. – a mechanism to specify variables to be

protected in critical region.– conditions under which a critical section may

be entered

designated critical section region //always assoc w/ resourcevariable protection resource

resource R1name: v1, v2, … Vn R2name: v1, v2, …, Vn

region R1name when B do S end• // B: condition of entry: boolean expression• // S: statement list• // region statements are scattered through the program. Region

statements can be nested, but CAUTION: deadlocks become very likely.

Example

• Unisex bathroom problem– single bathroom– 2 processes types: male and female

• constraints– No male & Female in BR at same time, multi-

male ok, multi-female ok.

resource bathroom: fc=0, mc=0; //protected variables (male count = mc, etc)

• //Note male and female solution is the same, expect switch mc and fc

male() region bathroom when fc = 0 do

mc ++;use bathroommc --;

end

• Problem– The region statement at any one time is only for

only one process to be in the region, so multi-male (multi-female) constraints fails

Try #2

males()

region bathroom when fc==0 do

mc++

end

use bathroom

region bathroom when true do

mc--

end

• Allows concurrency. Second part, protect, so only one process accessing mc at the same time (mutual exclusion).

• problem: starvation

Dinning Philosophers (again)

resource eat: forks[5] ={0,0,0,0,0}

philosopher(i)

//assume left and right are defined

think();

region eat when ((fork[left] ==0)&&(fork[right]==0) do

fork[left] =1; fork[right]=1;

end

eating();

region eat when true do

fork[left] =0; fork[right]=0;

end

Monitors

• A monitor is a collection of procedures and variables and data structures– Processes can access these variables only by invoking the

procedures in the monitor– At most 1 process can be active at any time in the monitor. This

provides mutual exclusion

– Unlike CCR’s the code is NOT scattered. It is in the monitor • condition variables provided: 2 operations defined

– wait(c): causes the process to block on C – and the processes leaves the monitor

– signal(c): unblocks one of the processes. The unblocked process runs immediately.

Monitors

• Only one process may be active within the monitor at a timemonitor monitor-name{

// shared variable declarationsprocedure P1 (…) { …. }

procedure Pn (…) {……}

Initialization code ( ….) { … }…

}}

Schematic view of a Monitor

Condition Variables• condition x, y;

• Two operations on a condition variable:– x.wait ()

• a process that invokes the operation is suspended.

– x.signal ()• resumes one of processes (if any) that invoked

x.wait ()

Monitor with Condition Variables

Condition Variables Choices

• If process P invokes x.signal (), with Q in x.wait () state, what should happen next?– If Q is resumed, then P must wait

• Options include– Signal and wait – P waits until Q leaves monitor or waits for another

condition– Signal and continue – Q waits until P leaves the monitor or waits for

another condition

– Both have pros and cons – language implementer can decide– Monitors implemented in Concurrent Pascal compromise

• P executing signal immediately leaves the monitor, Q is resumed– Implemented in other languages including Mesa, C#, Java

Monitor m_namevariable1, …procedure P1( …)

statementsendprocedure P2(…)

statementsendbegin// initialization code for variablesend

end m_name // invoked by: m_name.P1(…)

Unisex (again)

• Bathroom problem again but cure starvation by alternate the sexes if op. sex is waiting

• int mc, fc, mcw, fcw – // mcw,fcw male/female count waiting

• condition fq, mq– // mq/fq male/female queue of processes blocked

procedure enter_bathroom_f( )if (mc == 0 && mcw == 0)

fc ++else

fcw ++ // waiting female countwait(fq) // blocks femalesfcw – // not waiting any morefc ++ // now female is in the bathroomsignal(fq) // signal next female, if none,

// signal is lost, but ok.

procedure exit_bathroom_m( )

// like reader/writer the last male leaving must

// signal to female that he has left.

mc -- // male left

if (mc == 0 && fcw !=0)

// last male out and female waiting

// signal wakes up just one process.

signal (fq)

// no other process can interrupt this since the monitor gives exclusive use

// to each procedure as each fq is executed, it runs immediately

• So 2 queues are waiting to enter monitor• If only male signal process executes the signal(fq),

then processes gets stuck on the queue and everything starts to block– deadlock, so we need to have female signaled to signal

the next female to enter.– Also, since signal is last statement, male does not block

it is finished.

• Each condition has its own queue

Solution to Dining Philosophersmonitor DP {

enum { THINKING; HUNGRY, EATING) state [5] ;condition self [5];

void pickup (int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self [i].wait;}

void putdown (int i) { state[i] = THINKING;

// test left and right neighbors test((i + 4) % 5); test((i + 1) % 5);

}

Solution to Dining Philosophers (2)

void test (int i) { if ( (state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING ;

self[i].signal () ; } }

initialization_code() { for (int i = 0; i < 5; i++) state[i] = THINKING;}

}

Solution to Dining Philosophers (3)

• Each philosopher I invokes the operations pickup() and putdown() in the following sequence:

Philospher(int i) { while (1) {

Think()dp.pickup (i)eat()dp.putdown (i)

} }

Solution to Dinning Philosophers (4)

• Is Deadlock Possible?

• Is Starvation possible?

QA&