Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
OS_REDACTED_БИЛЕТЫ.docx
Скачиваний:
9
Добавлен:
01.04.2022
Размер:
1.15 Mб
Скачать
  1. Решите задачу обедающих философов через семафоры и мьютексы

Пять философов сидят за круглым столом, и у каждого из них есть тарелка спагетти. Эти спагетти настолько скользкие, что есть их можно только двумя вилками. Между каждыми двумя тарелками лежит одна вилка.

Решение не вызывает взаимной блокировки и допускает максимум параллелизма для произвольного числа философов. В нем используется массив state, в котором отслеживается, чем занимается философ: ест, размышляет или пытается поесть (пытается взять вилки). Перейти в состояние приема пищи философ может, только если в этом состоянии не находится ни один из его соседей. Соседи философа с номером i определяются макросами LEFT и RIGHT. Иными словами, если i равен 2, то LEFT равен 1, а RIGHT равен 3.

#define N 5 /* количество философов */

#define LEFT (i+N−1) %N /* номер левого соседа для i-го философа */

#define RIGHT (i+1) %N /* номер правого соседа для i-го философа */

#define THINKING 0 /* философ размышляет */

#define HUNGRY 1 /* философ пытается взять вилки */

#define EATING 2 /* философ ест спагетти */

typedef int semaphore; /* Семафоры — особый вид целочисленных переменных */

int state[N]; /* массив для отслеживания состояния каждого философа */

semaphore mutex = 1; /* Взаимное исключение входа в критическую область */

semaphore s[N]; /* по одному семафору на каждого философа */

void philosopher(int i) /* i – номер философа (от 0 до N−1) */

{

while (TRUE) { /* бесконечный цикл */

think(); /* философ размышляет */

take_forks(i); /* берет две вилки или блокируется */

eat(); /* ест спагетти */

put_forks(i); /* кладет обе вилки на стол */

} }

void take_forks(int i) /* i – номер философа (от 0 до N−1) */

{

down(&mutex); /* вход в критическую область */

state[i] = HUNGRY; /* запись факта стремления философа поесть */

test(i); /* попытка взять две вилки */

up(&mutex); /* выход из критической области */

down(&s[i]); /* блокирование, если вилки взять не удалось */ }

void put_forks(i) /* i – номер философа (от 0 до N−1) */ {

down(&mutex); /* вход в критическую область */

state[i] = THINKING; /* философ наелся */

test(LEFT); /* проверка готовности к еде соседа слева */

test(RIGHT); /* проверка готовности к еде соседа справа */

up(&mutex); /* выход из критической области */

}

void test(i) /* i – номер философа (от 0 до N−1) */ {

if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {

state[i] = EATING;

up(&s[i]); } }

В программе используется массив семафоров, по одному семафору на каждого философа, поэтому голодный философ может блокироваться, если нужная ему вилка занята. Обратите внимание на то, что каждый процесс в качестве своей основной программы запускает процедуру philosopher, но остальные процедуры: take_forks, put_ forks и test — это обычные процедуры, а не отдельные процессы.

  1. Решите задачу производителя/потребителя через команду TSL

Поставить enter_region вместо down вход в критическую область

Поставить leave region вместо up выход из критической области

  1. Решите задачу обедающих философов через команду TSL

Поставить enter_region вместо down вход в критическую область

Поставить leave region вместо up выход из критической области

  1. Решите задачу читателя/писателя через команду TSL

Поставить enter_region вместо down вход в критическую область

Поставить leave region вместо up выход из критической области

  1. Решите задачу читателя/писателя через семафоры и мьютексы

Задача читателей и писателей: моделируется доступ к базе данных. Представим, к примеру, систему бронирования авиабилетов, в которой есть множество соревнующихся процессов, желающих обратиться к ней для чтения и записи. Вполне допустимо наличие нескольких процессов, одновременно считывающих информацию из базы данных, но, если один процесс обновляет базу данных (проводит операцию записи), никакой другой процесс не может получить доступ к базе данных даже для чтения информации.

В коде первый читатель для получения доступа к базе данных выполняет в отношении семафора db операцию down. А все следующие читатели просто увеличивают значение счетчика rc. Как только читатели прекращают свою работу, они уменьшают значение счетчика, а последний из них выполняет в отношении семафора операцию up, позволяя заблокированному писателю, если таковой имеется, приступить к работе. В представленном здесь решении есть одна достойная упоминания недостаточно очевидная особенность. Допустим, что какой-то читатель уже использует базу данных и тут появляется еще один читатель. Поскольку одновременная работа двух читателей разрешена, второй читатель допускается к базе данных. По мере появления к ней могут быть допущены и другие дополнительные читатели. Теперь допустим, что появился писатель. Он может не получить доступа к базе данных, поскольку писатели должны иметь к ней исключительный доступ, поэтому писатель приостанавливает свою работу. Позже появляются и другие читатели. Доступ дополнительным читателям будет открыт до тех пор, пока будет активен хотя бы один читатель. Вследствие этой стратегии, пока продолжается наплыв читателей, все они будут получать доступ к базе по мере своего прибытия. Писатель будет приостановлен до тех пор, пока не останется ни одного читателя. Если новый читатель будет прибывать, скажем, каждые 2 с и каждый читатель затратит на свою работу по 5 с, писатель доступа никогда не дождется. Чтобы предотвратить такую ситуацию, программу можно слегка изменить: когда писатель находится в состоянии ожидания, то вновь прибывающий читатель не получает немедленного доступа, а приостанавливает свою работу и встает в очередь за писателем. При этом писатель должен дождаться окончания работы тех читателей, которые были активны при его прибытии, и не должен пережидать тех читателей, которые прибывают после него. Недостаток этого решения заключается в снижении производительности за счет меньших показателей параллельности в работе.

typedef int semaphore; /* напрягите свое воображение */

semaphore mutex = 1; /* управляет доступом к 'rc' */

semaphore db = 1; /* управляет доступом к базе данных */

int rc = 0; /* количество читающих или желающ. читать процессов*/

void reader(void)

{

while (TRUE) { /* бесконечный цикл */

down(&mutex); /* получение исключительного доступа к ‘rc’ */

rc = rc + 1; /* теперь на одного читателя больше */

if (rc == 1) down(&db); /* если это первый читатель... */

up(&mutex); /* завершение исключительного доступа к ‘rc’ */

read_data_base( ); /* доступ к данным */

down(&mutex); /* получение исключительного доступа к ‘rc’ */

rc = rc − 1; /* теперь на одного читателя меньше */

if (rc == 0) up(&db); /* если это последний читатель... */

up(&mutex); /* завершение исключительного доступа к ‘rc’ */

use_data_read(); /* некритическая область */ } }

void writer(void)

{

while (TRUE) { /* бесконечный цикл */

think_up_data( ); /* некритическая область */

down(&db); /* получение исключительного доступа */

write_data_base( ); /* обновление данных */

up(&db); /* завершение исключительного доступа */

} }