Dining Philosophers Problem: Algorithms to Prevent Starvation and Deadlock, Lecture notes of Design

The Dining Philosophers problem is a classic computer science problem that uses a metaphor of philosophers eating spaghetti to illustrate synchronization issues in concurrent systems. various algorithms to prevent starvation and deadlock in this context. The problem statement is provided, followed by an explanation of a simple but incorrect approach, and Tannenbaum's solution that maximizes concurrency. The document also touches upon deadlock and its conditions, as well as reactions to deadlock by an operating system.

Typology: Lecture notes

2021/2022

Uploaded on 09/27/2022

sheetal_101
sheetal_101 🇺🇸

4.8

(17)

234 documents

1 / 6

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
AClassic Problem - Dining Philosophers
The Dining Philosophers problem is a classic OS problem that’susuallu stated in very non-OS terms:
There are Nphilosphers sitting around a circular table eating spaghetti and discussing philos-
phy. The problem is that each philosopher needs 2 forks to eat, and there are only Nforks, one
between each 2 philosophers. Design an algorithm that the philosophers can followthat
insures that none starves as long as each philosopher eventually stops eating, and such that the
maximum number of philosophers can eat at once.
Whydescribe problems this way? Well, the analogous situations in computers are sometimes so
technical that theyobscure creative thought. Thinking about philosophers makes it easier to think
abstractly.And manyofthe early students of this field were theoreticians who likeabstract problems.
There are a bunch of named problems - Dining Philosophers, Drinking Philiosophers, Byzantine Generals,
etc.
Here’sanapproach to the Dining Phils1that’ssimple and wrong:
void philosopher() {
while(1) {
sleep();
get_left_fork();
get_right_fork();
eat();
put_left_fork();
put_right_fork();
}
}
If every philosopher picks up the left fork at the same time, noone gets to eat - ever.
Some other suboptimal alternatives:
•Pick up the left fork, if the right fork isn’tavailable for a giventime, put the left fork down,
wait and try again. (Big problem if all philosophers wait the same time - we get the same fail-
ure mode as before, but repeated.) Even if each philosopher waits a different random time, an
unluckyphilosopher may starve(in the literal or technical sense).
•Require all philosophers to acquire a binary semaphore before picking up anyforks. This
guarantees that no philosopher starves (assuming that the semaphore is fair) but limits parallel-
ism dramatically.(FreeBSD has a similar problem with multiprocessor performance).
Here’sTannenbaum’sexample, that gets maximum concurrency:
1Nowthat you’ve seen the problem, you can call them Phil, too.
pf3
pf4
pf5

Partial preview of the text

Download Dining Philosophers Problem: Algorithms to Prevent Starvation and Deadlock and more Lecture notes Design in PDF only on Docsity!

A Classic Problem - Dining Philosophers

The Dining Philosophers problem is a classic OS problem that’s usuallu stated in very non-OS terms: There are N philosphers sitting around a circular table eating spaghetti and discussing philos- phy. The problem is that each philosopher needs 2 forks to eat, and there are only N forks, one between each 2 philosophers. Design an algorithm that the philosophers can follow that insures that none starves as long as each philosopher eventually stops eating, and such that the maximum number of philosophers can eat at once. Why describe problems this way? Well, the analogous situations in computers are sometimes so technical that they obscure creative thought. Thinking about philosophers makes it easier to think abstractly. And many of the early students of this field were theoreticians who like abstract problems. There are a bunch of named problems - Dining Philosophers, Drinking Philiosophers, Byzantine Generals, etc.

Here’s an approach to the Dining Phils^1 that’s simple and wrong: void philosopher() { while(1) { sleep(); get_left_fork(); get_right_fork(); eat(); put_left_fork(); put_right_fork(); } }

If every philosopher picks up the left fork at the same time, noone gets to eat - ever.

Some other suboptimal alternatives:

  • Pick up the left fork, if the right fork isn’t available for a given time, put the left fork down, wait and try again. (Big problem if all philosophers wait the same time - we get the same fail- ure mode as before, but repeated.) Even if each philosopher waits a different random time, an unlucky philosopher may starve (in the literal or technical sense).
  • Require all philosophers to acquire a binary semaphore before picking up any forks. This guarantees that no philosopher starves (assuming that the semaphore is fair) but limits parallel- ism dramatically. (FreeBSD has a similar problem with multiprocessor performance). Here’s Tannenbaum’s example, that gets maximum concurrency:

(^1) Now that you’ve seen the problem, you can call them Phil, too.

#define N 5 /* Number of philosphers */ #define RIGHT(i) (((i)+1) %N) #define LEFT(i) (((i)==N)? 0 : (i)+1)

typedef enum { THINKING, HUNGRY, EATING } phil_state;

phil_state state[N]; semaphore mutex =1; semaphore s[N]; /* one per philosopher, all 0 */

void test(int i) { if ( state[i] == HUNGRY && state[LEFT(i)] != EATING && state[RIGHT(i)] != EATING ) {state[i] = EATING; V(s[i]);} }

void get_forks(int i) { P(mutex); state[i] = HUNGRY; test(i); V(mutex); P(s[i]); }

void put_forks(int i) { P(mutex); state[i]= THINKING; test(LEFT(i)); test(RIGHT(i)); V(mutex); }

void philosopher(int process) { while(1) { think(); get_forks(process); eat(); put_forks(process); } }

The magic is in the test routine. When a philosopher is hungry it uses test to try to eat. If test fails, it wiats on a semaphore until some other process sets its state to EATING. Whenever a philosopher puts down forks, it invokes test in its neighbors. (Note that test does nothing if the process is not hun- gry, and that mutual exclusion prevents races.)

So this code is correct, but somewhat obscure. And more importantly, it doesn’t encapsulate the philosopher - philosophers manipulate the state of their neighbors directly. Here’s a version that does not require a process to write another process’s state, and gets equivalent parallelism.

#define N 100 /* Number of free slots */

semaphore mutex = 1; semaphore empty = N; semaphore full = 0;

void producer() { item i;

while (1) { i = produce_item(); P(mutex); P(empty); insert_new_item(i); V(mutex); V(full); } }

void consumer() { item i;

while(1) { P(full); P(mutex); i = get_next_item(); V(mutex); V(empty); consume_item(i); } }

When the producer arrives at a full queue, it will block on empty while holding mutex, and the consumer will subsequently block on mutex forever. Graphically, this looks like:

Process a

Resource 1

Process b

Resource 2

Wants

Wants

Has

Has

Processes we hopefully understand. Resources are the other parts of the computer system - hardware and software. For the purposes of deadlock, we’re concerned with resources that require exclusive control: tape drives, CD-ROM burners, locks & semaphores, process table slots, etc.

4 Conditions

More formally there are 4 conditions for deadlock:

  • Mutual Exclusion: Resources can only be held by at most one process. A process canniot deadlock on a sharable resource.^2
  • Hold and Wait: A process can hold a resource and block waiting for another.
  • No Preemption: The operating system cannot take a resource back from a process that has it.
  • Circular Wait: A process is waiting for a resource that another process has which is waiting for a resource that the first has. This generalizes. If these 4 conditions are necessary and sufficient. (If these four appear, there is a deadlock, if not no deadlock.) The problem is that the chain in a circular wait can be long and complex. We use resource graphs to help us visualize deadlocks.

A

S

Requesting a resource

B

T

Holding a resource

All four conditions are can be expressed in the graph:

  • Mutual Exclusion: Only one one outgoing arrow on a resource.
  • Hold and Wait: Processes can simultaneously have incoming and outgoing arrows.
  • No Preemption: Only processes without outgoing lines can delete their incoming lines (only processes not waitig can release resources).
  • Circular Wait: A directed cycle in the graph.

A Deadlock Example

Consider a set of processes making the following requests: A B C Request R Request S Request T Request S Request T Request R Release R Release S Release T Release S Release T Release R

Here’s a possible deadlock scenario:

(^2) This related to the mutual exclusion used to avoid race conditions. Using locks or semaphores makes the code between the P and the V a mutually exclusive resource.