- •5. Взаимодействие и синхронизация процессов
- •5.1. Взаимодействие процессов (Лекция 9)
- •5.1.1. Способы взаимодействия процессов
- •5.1.1.1. Проблема синхронизации
- •5.1.1.2. Критические секции, средства коммуникации процессов
- •5.1.1.3. Способы взаимодействия процессов, понятия взаимного исключения, взаимной блокировки, голодания процессов
- •5.1.2. Взаимные исключения
- •5.1.2.1. Требования к взаимным исключениям
- •5.1.2.2. Алгоритмы Деккера и Петерсона реализации взаимного исключения
- •5.2. Семафоры и другие средства синхронизации (Лекция 10)
- •5.2.1. Синхронизация задач с помощью семафоров
- •5.2.1.1. Семафоры Дейкстры
- •5.2.1.2. Условная синхронизация с помощью семафоров
- •5.2.1.3. Планирование очереди процессов, ожидающих у семафора
- •5.2.2. Другие средства синхронизации: счётчики событий, секвенсоры, мониторы, передача сообщений
- •5.2.2.1. Счетчики событий и секвенсоры
- •5.2.2.2. Объекты синхронизации, регламентированные posix
- •5.2.2.3. Мониторы Хоара
- •5.2.2.4. Передача сообщений
- •5.3. Классические проблемы межпроцессного взаимодействия (Лекция 11)
- •5.3.1. Проблема "обедающих философов"
- •5.3.1.1. Описание проблемы "обедающих философов"
- •5.3.1.2. Решение (алгоритм) проблемы "обедающих философов"
- •5.3.2. Проблема "спящего брадобрея" (задача о парикмахерской)
- •5.3.2.1. Описание задачи о парикмахерской
- •5.3.2.2. Решение (алгоритм) задачи о парикмахерской
- •5.3.3. Задача "читателей и писателей"
- •5.3.3.1. Описание задачи читателей и писателей
- •5.3.3.2. Решение (алгоритм) задачи читателей и писателей
- •5.4. Взаимоблокировки (Лекция 12)
- •5.4.1. Возникновение взаимоблокировок
- •5.4.1.1. Проблема взаимоблокировок
- •5.4.2. Устранение взаимоблокировок
- •5.4.2.1. Запрещение запуска процесса
- •5.4.2.2. Запрет выделения ресурса
- •5.4.3. Обнаружение взаимоблокировок
- •5.4.3.1. Алгоритм обнаружения взаимоблокировок
- •5.4.3.2. Действия, выполняемые после обнаружения взаимоблокировки
5.1.1.3. Способы взаимодействия процессов, понятия взаимного исключения, взаимной блокировки, голодания процессов
Способы взаимодействия процессов можно классифицировать по степени осведомленности одного процесса о состоянии другого. В случае, когда процессы не осведомлены друг о друге, между ними имеет место конкуренция. Результат работы каждого процесса не зависит при этом от результатов работы других процессов, но возможно влияние одного процесса на время работы другого. Потенциальная проблема, возникающая в случае конкуренции процессов – необходимость взаимных исключений (mutualexclusion), если два или большее число процессов требуют доступ к одному неразделяемому ресурсу (см. п. 5.1.1.2). Осуществление взаимных исключений порождает две проблемы:
1) взаимную блокировку – ситуация, в которой несколько процессов занимают ресурсы, и каждый из них для продолжения работы должен получить доступ к занятому ресурсу из данного множества;
2) голодание – несколько процессов попеременно пытаются занимать некоторый ресурс, но какие-либо из них не могут получить доступ к ресурсу (например, низкоприоритетные) и постоянно находятся в состоянии ожидания доступа.
Если процессы косвенно осведомлены о наличии друг друга (разделяют доступ к некоторому объекту, например, буферу ввода/вывода), имеет место сотрудничество с использованием разделения. Результат работы одного процесса может зависеть от информации, полученной от других. Возможно влияние одного процесса на время работы других. При этом также возможны взаимное исключение, взаимоблокировка и голодание. Кроме того, существует проблема связи данных.
Когда процессы непосредственно осведомлены друг о друге между ними имеет место сотрудничество с использованием связи. Результат работы одного процесса может зависеть от информации, полученной от других. Также возможно влияние одного процесса на время работы других. Проблемы – взаимоблокировка и голодание.
5.1.2. Взаимные исключения
5.1.2.1. Требования к взаимным исключениям
Взаимные исключения должны осуществляться в принудительном порядке. В любой момент времени из всех процессов, имеющих критический раздел для одного и того же ресурса или разделяемого объекта, в этом разделе может находиться только одни процесс.
Процесс, завершающий работу в некритическом разделе, не должен влиять на другие процессы.
Не должна возникать ситуация бесконечного ожидания доступа к критическому ресурсу (взаимоблокировка и голодание).
Когда в критическом разделе нет ни одного процесса, любой процесс, запросивший возможность входа в него, должен немедленно его получить.
Не делается никаких предположений о количестве процессов или их относительных скоростях работы.
Процесс остаётся в критическом разделе только в течение ограниченного количества времени.
Первый подход к выполнению перечисленных требований является программным и заключается в соблюдении всех требований самим процессом, без поддержки ОС (алгоритмы Деккера, Петерсона). Второй подход состоит в использовании машинных команд специального назначения.
5.1.2.2. Алгоритмы Деккера и Петерсона реализации взаимного исключения
Алгоритм Деккера (см. ниже) предполагает только элементарные взаимоисключения на уровне доступа к памяти, т.е. одновременный доступ (чтение и/или запись) упорядочивается при помощи некоторого механизма. Необходимо "навязать" определенный порядок действия двум процессам. Переменная turnуказывает, какой из двух процессов имеет право на вход в критическую секцию. Когда процессp0 намерен войти в критическую секцию, он устанавливаетflag[0] вtrueи проверяетflag[1]. Еслиflag[1] =false,p0 может немедленно войти в критическую секцию, иначеp0 проверяетturn. Еслиturn= 0, то сейчас – очередьp0 иp0 периодически проверяетflag[1].P1, в свою очередь, обнаружив, что сейчас не его очередь, в какой-то момент времени сбрасываетflag[1] вfalse. По окончанию работыp0 сбрасывает flag[0] вfalseи присваивает единицу переменнойturn.
Алгоритм:
Boolean flag[2];
Int turn;
Voidp0 ( )
{
While (true)
{
Flag[0] = true;
While (flag[1])
If (turn == 1)
{
Flag[0] = false;
While (turn == 1); //ничего не делать
Flag[0] = true;
}
//критический раздел
turn= 1;
flag[0] =false;
//остальной код
}
}
voidp1 ()
{
//определение аналогично предыдущему, с заменой всех переменных на
//противоположные
}
void main ( )
{
flag[0] = false;
flag[1] = false;
turn = 1;
parbegin (p0, p1);
}
Алгоритм Петерсона реализации взаимного исключения изобретен в 1981 г. С этого момента алгоритм Деккера считается устаревшим.
#define FALSE 0
#define TRUE 1
#define N 2
intturn; /*чья сейчас очередь*/
int interested[N]; /*изначально все нули - FALSE*/
void enter_region (int process)
{
int other = 1-process; /*противоположный*/
interested[process]=TRUE; /*да, мы хотим войти*/
turn=process;
while(turn==process&&interested[other]==true); /*активное ожидание*/
}
void leave_region(int process)
{
interested[process]=FALSE; /*все, мы уходим*/
}
Примечание: оптимизирующий компилятор может кэшировать переменные в регистрах, тогда код не будет работать. Следует использовать volatile.
Активное ожидание – плохо. Кроме того, возникает проблема инверсии приоритета. Допустим, есть 2 процесса: HиL, причем приоритет Н>>L. Если Н оказался в ожидании, покаLне освободит некий ресурс, то Н в активном ожидании постоянно отнимает процессорное время у низкоприоритетного процессаL, не давая ему выйти из критической секции.