
Задание 1.
Процесс 1 |
Процесс 2 |
process P1 ; begin ... While true do begin … request ( R1, 1) ; ... request ( R2, 1) ; ... release ( R2, 1) ; ... release ( R1, 1) ; ... end ... end. |
process P2 ; begin ... While true do begin … request ( R2, 1) ; ... request ( R1, 1) ; ... release ( R2, 1) ; ... release ( R1, 1) ; ... end ... end. |
Мы построили граф состояний системы. В данном случае тупик возможен. Тупиковая ситуация выделена.
Задание
2
Задание 3. Решить программным способом задачу взаимного исключения. Являются ли приведенные ниже процедуры решением поставленной задачи? Если нет, объясните, когда могут возникнуть ошибки?
Решение:
process INIT; common boolean C1. C2;
begin С1:=false; С2:=false; start(P1); start(P2); end;
process P1;
Begin
while true do
begin BEFORE1; C1:=true; while C2 do ; CS1; C1:=false; AFTER1; end
end;
process P2;
Begin
while true do
begin BEFORE2; while C1 do ; C2:=true; CS1; C2:=false; AFTER2; end
end;
Данное решение не гарантирует, что оба процесса не могут одновременно оказаться в критической секции. Приведем контрпример:
В начальный момент C1=false. Процесс P2 пройдет цикл while C1 do ; После этого запустится процесс P1 и выполнится присваивание C1:=true; и цикл while C2 do ; и первый процесс беспрепятственно прошел в критическую секцию. После этого продолжается процесс P2. Цикл он уже прошел, поэтому присваивает C2:=true и сам тоже попадает в критическую секцию.
Эту задачу можно решить, используя процедуру перевода процесса в состояние задержки, которую мы рассмотрим ниже.
Задание 4. Решить задачу взаимного исключения, используя логические переменные и процедуру перевода процесса в состояние задержки (приостановки) Delay(T), где параметр T задает время задержки процесса (время, на которое он выводится из конкуренции за время процессора).
Решение:
process INIT; common boolean C1. C2;
begin С1:=false; С2:=false; start(P1); start(P2); common real T1, T2; end;
process P1;
В данном случае процесс сначала
проверяет, свободна ли критическая
секция, если нет, то он ждет рандомное
время до возобновления выполнения.
Аналогично поступает второй процесс.
Задачу взаимного исключения данная
программа решает, но возникает другая
проблема: неэкономное использование
времени процессора, ведь мы не знаем
заранее, какое время будет ожидать
процесс, может получиться, что он будет
простаивать, в то время как мог бы уже
зайти в критическую секцию и выполнить
все необходимое.
while true do
begin BEFORE1;
C1:=true;
while C2 do
begin
C1:=false;
T1:=random;
delay(T1);
C1:=true;
end;
CS1; C1:=false;
AFTER1;
end
end;
process P2;
Begin
while true do
begin BEFORE2;
C2:=true;
while C1 do
begin
C2:=false;
T2:=random;
delay(T2);
C2:=true;
end;
CS1; C2:=false;
AFTER2;
end
end;
Задание 5. Алгоритм Деккера можно сформулировать следующим образом:
Чтобы организовать работу N процессов нужно завести очередь, в которой будут храниться все процессы желающие получить доступ к КС. Для облегчение работы с очередью создадим несколько процедур: add (N,key) ( добавляет в конец очереди элемент со значением N и индивидуальным ключом key), del (key) (проверяет, совпадает ли ключ головного элемента очереди с key и удаляет, если совпал) и две функции: check(N) (сравнивает номер процесса в голове списка с N. Выдает true если они различны) и numhead ( возвращает номер первого элемента очереди). Уникальный ключ нужен, чтобы процесс мог удалить из очереди только себя, я не какой-либо другой процесс. Каждый процесс добавляет в очередь только себя. Вместо C1, … ,CN заведем массив mas булевского типа.
Процедура инициализации |
Первый процесс |
N-ый процесс |
|
procedure INIT; common mas; common integer N ; integer i; begin for i:=1 to 2000 do mas[i]:=false; Key:=random+1; add (1,key) start(P1,key) ; … Key:=random+1; start(Pn,key) end INIT .
|
process P1(key:int64); common mas ; begin while true do begin BEFORE1 ; add(1,key); mas[1] := true ; while check(1) do begin if numhead<>1 then begin mas[1] := false ; while numhead <>1 do ; mas[1] := true ; end end ; CS1 ; del (key); mas[1] := false; AFTER1 ; end end P1 . |
process Pn(key:int64); common mas; begin while true do begin BEFOREn ; add(N,key); mas[N] := true ; while check(N) do begin if numhead<>N then begin mas[N] := false ; while numhead <>N do ; mas[N] := true ; end end ; CSn ; del (key); mas[N] := false; AFTERn ; end end Pn . |
Задание 6. Решается ли в приведенных ниже программах проблема взаимного исключения (random – процедура, генерирующая случайное число, delay(T) – процедура задержки процесса на время T, в течение которого процесс не будет конкурировать за время процессора)?
process INIT; common boolean C1, C2; common integer N; common real T1, T2;
begin С1:=false; С2:=false; T1:= random; T2:= random; N:=1; start(P1); start(P2); end;
process P1;
Begin
while true do
begin BEFORE1;
C1:=true;
while C2 and (N<>1) do delay(T1);
CS1;
C1:=false; N:=2;
AFTER1;
end
end;
process P2;
Begin
while true do
begin BEFORE2;
C2:=true;
while C1 and (N<>2) do delay(T2);
CS2;
C2:=false; N:=1;
AFTER2;
end
end;
Допустим, второй процесс попробует зайти в критическую секцию. С1 еще false, поэтому он пройдет While и встанет перед критической секцией. В это время пойдет первый процесс. Т.к. N=1, то он тоже пройдет через while. После этого оба процесса могут попасть в критическую секцию.
Задание 7 : написать процедуры, моделирующие семафорные примитивы для общего семафора (допускаются только неотрицательные значения целочисленной переменной, моделирующей считающий семафор). При написании процедур можно использовать бинарные семафоры. Ниже приведен код процедур PP и VP, которые моделируют работу семафорных примитивов для общего семафора в соответствии с поставленной задачей. Решена ли задача? Найти в приведенном ниже псевдокоде ошибки, объяснить и исправить их (привести примеры, иллюстрирующие возможные проблемы), если они есть.
1 Решение:
Init: proc (var S: integer; value: integer);
common binary semaphore B1, B2;
begin B1:=1; B2:=1; S:=value end;
PP: procedure (var S: integer); common binary semaphore B1, B2;
Begin P(B2); P(B1); S:=S–1; if S>0 then V(B2); V(B1) end;
VP: procedure (var S: integer); common binary semaphore B1, B2;
Begin P(B1); S:=S+1; V(B1); V(B2 ) end;
Задача не решена, т.к. значение S может стать отрицательным. Пример: пусть у нас есть две единицы ресурса и N процессов, где N>2. Тогда каждый процесс может сделать один вызов PP, после этого вызвать VP и остановиться в VP перед V(B2);. Дальше, процессы по очереди вызовут PP и спокойно пройдут через P(B2), завершая каждый VP, остановятся перед P(B1); Дальше, каждый процесс может завершить PP, уменьшив S на единицу, т.е. S станет отрицательным, т.к. N>2 , что противоречит условию.
2
В данном случае, использоваться может
лишь одна единица ресурса. Первый
процесс получит эту единицу, а все
остальные запросы упрутся в P(B2)
пока первый процесс не освободит свою
единицу, а если ему самому понадобится
ещё ресурс, тогда вообще будет тупик.
Init: proc (var S: integer; value: integer);
common binary semaphore B1, B2;
begin B1:=1; B2:=1; S:=value end;
PP: procedure (var S: integer);
common binary semaphore B1, B2;
Begin P(B2); P(B1); S:=S–1; V(B1) end;
VP: procedure (var S: integer);
common binary semaphore B1, B2;
Begin P(B1); S:=S+1; V(B1); V(B2 ) end;
3 Решение:
I
Пусть ресурсов K, а
процессов N>K.
Каждый процесс зайдет сначала в PP,
потом в VP и встанет перед
V(B2). Затем,
каждый процесс вызовет PP,
пройдет P(B2),
завершая VP, пройдет
условие while
S<=0 do;
и остановится перед P(B1).
После того, как все процессы соберутся
перед P(B1)
они могут завершить PP,
сделав S отрицательным,
т.к. N>K.
common binary semaphore B1, B2;
begin B1:=1; B2:=1; S:=value end;
PP: procedure (var S: integer);
common binary semaphore B1, B2;
Begin P(B2); while S<=0 do; P(B1); S:=S–1; V(B1); V(B2) end;
VP: procedure (var S: integer);
common binary semaphore B1, B2;
Begin P(B1); S:=S+1; V(B1); V(B2 ) end;
4
Этот вариант вообще не соответствует
принципам работы общего семафора, т.к.
PP должен дождаться
освобождения ресурса и сам его взять,
а он вместо этого ничего не делает и
завершает работу. Если процесс «думает»,
что после PP он может
получить доступ к ресурсу (КС) и что он
потом этот ресурс должен отдать, то
вызовов PP и VP
будет одинаковое кол-во,
Но тогда, например, при двух единицах
ресурса вначале после трех вызовов PP
а потом трех VP в итоге
получим S=3, т.е. ресурсов
якобы стало больше, но на самом-то деле
не стало.
Init: proc (var S: integer; value: integer);
common binary semaphore B;
begin B:=1; S:=value end;
PP: procedure (var S: integer);
common binary semaphore B;
Begin P(B); if S>0 then S:=S–1; V(B) end;
VP: procedure (var S: integer);
common binary semaphore B;
Begin P(B); S:=S+1; V(B) end;
Поставленную в начале задачу можно решить посредством третьего семафора:
Решение:
Init: proc (var S: integer; value: integer);
common binary semaphore B1, B2, B3;
begin B1:=1; B2:=1; B3:=1; S:=value end;
PP: procedure (var S: integer); common binary semaphore B1, B2;
Begin P(B3); P(B2); P(B1); S:=S–1; if S>0 then V(B2); V(B1); V(B3) end;
VP: procedure (var S: integer); common binary semaphore B1, B2;
Begin P(B1); S:=S+1; V(B1); V(B2 ) end;
Таким образом внутри P(B3); V(B3) сможет находиться только один процесс, что позволяет условию if S>0 then V(B2); эффективно работать, задерживая на P(B2); процессы, в случае нехватки ресурсов, до их освобождения.
Задание 8: написать процедуры, моделирующие семафорные примитивы для общего семафора (допускаются любые значения целочисленной переменной, моделирующей считающий семафор). При написании процедур можно использовать бинарные семафоры. Ниже приведен код процедур PP и VP, которые моделируют работу семафорных примитивов для общего семафора в соответствии с поставленной задачей. Решена ли задача? Найти в приведенном ниже псевдокоде ошибки, объяснить и исправить их (привести примеры, иллюстрирующие возможные проблемы), если они есть.