38
Операционные Системы 5. Синхронизация Брагин Алексей Владимирович [email protected] ИУ9, МГТУ им. Н.Э. Баумана

Операционные системы 2015, лекция № 5

Embed Size (px)

Citation preview

Операционные Системы

5. Синхронизация

Брагин Алексей Владимирович[email protected]

ИУ9, МГТУ им. Н.Э. Баумана

Критические секции

• Последовательность инструкций, одновременное выполнение которой может привести к неправильным результатам называется критической секцией

• Т.о. взаимное исключение (mutual exclusion)между двумя критическими секциями достаточно для корректного исполнения

• Один из способов гарантировать взаимное исключение – использовать блокировки (locks)

© 2013 Брагин А.В. 2

Пример КС

© 2013 Брагин А.В. 3

Поток1 Поток2 Поток1 Поток2 Поток1 Поток2

Некорректно Правильно Правильно

Критические секции 2

• В каких случаях возникают критические секции?– В одновременно выполняемом коде

– Действия прочитать-изменить-записать

– Общая переменная

• Общие переменные– Глобальные и выделенные из кучи переменные

– Любые другие не локальные переменные в стэке

© 2013 Брагин А.В. 4

Классический пример

• Функция снятия денег со счёта фирмы в банке (использовать только для своих счетов!)

• Предположим, что одновременно происходит выплата заработной платы 10000руб. с одного счёта двум сотрудникам. А на счету находится 100 000 руб.

© 2013 Брагин А.В. 5

void WithdrawMoney(account, amount) {int balance = GetBalance(account); // чтениеbalance -= amount; // изменениеSetBalance(account, balance); // записьGiveCashToUser();

}

Классический пример 2

• Программа в банке работает в многопоточном режиме

© 2013 Брагин А.В. 6

void WithdrawMoney(account, amount) {int balance = GetBalance(account);balance -= amount;SetBalance(account, balance);GiveCashToUser();

}

void WithdrawMoney(account, amount) {int balance = GetBalance(account);balance -= amount;SetBalance(account, balance);GiveCashToUser();

}

Классический пример 3

• Если многозадачность вытесняющая, то переключение контекста может произойти в любом месте

• Гарантировать, что три операции пройдут в нужной последовательности нельзя

• Даже такой простой код, как k++ не является безопасным! (если k – общая переменная)

© 2013 Брагин А.В. 7

Требования к критическим секциям

• Каким требованиям должна удовлетворять правильная реализация критических секций– Взаимное исключение

• Максимум один поток может быть в критической секции• Другой поток вне критической секции не может

вытеснить поток, находящийся в критической секции

– Ограниченное ожидание• Если поток ожидает входа в критическую секцию, то рано

или поздно это произойдёт

– Производительность• Накладные расходы на вход и выход из критических

секций малы по сравнению с работой, выполняемой внутри критических секций

© 2013 Брагин А.В. 8

Механизмы реализации КС

• Запрет прерываний– Самый простой, но очень много недостатков

• Алгоритм Петерсона• Спинлоки (spinlocks)

– Базовый примитив, часто используется для построения более сложных блокировок

• Семафоры (non-spinning locks)– Просты для понимания, но сложны в использовании

• Мониторы– Высокоуровневая блокировка, требует поддержки со стороны языка

программирования– Легче использовать

• Сообщения– Простая модель взаимодействия и синхронизации на основе

передаваемых сообщений через канал– Используется преимущественно в распределённых системах

© 2013 Брагин А.В. 9

Запрет прерываний

• Простейший подход: временно отключить прерывания

• Недостатки:– Доступно только ядру, режим пользователя не

может отключать прерывания

– Недостаточно на многопроцессорной системе (у каждого ЦП – свои прерывания)

– Длительный период отключения прерываний может привести к неработоспособности устройств

• Используется только в редких случаях

© 2013 Брагин А.В. 10

Алгоритм Петерсона• Работает для ограниченного числа процессов N (пример для 2)

• Не требует аппаратной поддержки атомарных операций

© 2013 Брагин А.В. 11

// Code based on Figure 2-24. Peterson’s solution for achieving mutual// exclusion (Tannenbaum, 2007, p. 123). Fixed by me.int turn; // Чья очередь?int interested[2]; // Исходно все значения равны FALSE

void EnterRegion(int process) // process – число от 0 до N-1{

int other = 1 – process; // Номер второго процессаinterested[process] = TRUE; // Обозначение интереса о входеturn = process; // Установка флагаwhile (turn == process && interested[other]); // Ожидание

}

void LeaveRegion(int process) // process, который выходит из КС{

interested[process] = FALSE; // Сообщаем о выходе из КС}

Блокировки/защёлки (locks)

• Защёлка – это объект с двумя операциями

– Lock(): т.е. войти в крит. секцию

– Unlock(): выйти из крит. секции

• Lock() блокирует дальнейшее выполнение потока до тех пор, пока не будет выполнен вход в крит. секцию

• Можно сказать, что поток держит эту защёлку, а потом освобождает её.

© 2013 Брагин А.В. 12

Как работает защёлка

© 2013 Брагин А.В. 13

Поток1 Поток2

Lock()

Unlock()

Unlock()

Lock()

Пример с банком и защёлкой

© 2013 Брагин А.В. 14

void WithdrawMoney(account, amount) {Lock(globalLock);int balance = GetBalance(account); // чтениеbalance -= amount; // изменениеSetBalance(account, balance); // записьUnlock(globalLock);GiveCashToUser();

}

Спинлок

• Циклическая блокировка, необходима аппаратная реализация

• Test-and-set

• Почему аппаратная?

© 2013 Брагин А.В. 15

typedef struct spinlock_s {int acquired = 0;

} spinlock_t;

void Lock(spinlock_t *s) {while (s->acquired);s->acquired = 1;

}

void Unlock(spinlock_t *s) {

s->acquired = 0;}

Реализация спинлока

• Внутри спинлока есть критическая секция

• Блокировка/разблокировка должна быть атомарной операцией– Атомарная – выполняется без прерывания,

либо всё, либо ничего

• Поэтому нужна аппаратная поддержка– Специальные инструкции: test-and-set,

compare-and-swap

– Временное отключение прерываний

© 2013 Брагин А.В. 16

Аппаратный test-and-set

• Внутри ЦП реализована следующая атомарная операция:

© 2013 Брагин А.В. 17

int TestAndSet(int *Val) {int OldValue = *Val;*Val = 1;return OldValue;

}

Реализация спинлока 2

• Используя такую аппаратную инструкцию, новая реализация спинлока:

© 2013 Брагин А.В. 18

typedef struct spinlock_s {int acquired = 0;

} spinlock_t;

void Lock(spinlock_t *s) {while (TestAndSet(&s->acquired));

}

void Unlock(spinlock_t *s) {s->acquired = 0;

}

Проблемы спинлока

• Неэффективны: если поток заблокирован спинлоком, то будет находиться в цикле до окончания кванта времени

• Спинлоки – примитивы для более высокоуровневой синхронизации

© 2013 Брагин А.В. 19

Высокоуровневая синхронизация

• Семафоры

• Мониторы (ещё одно значение слова монитор…)

• Тупиковые ситуации (deadlock)

© 2013 Брагин А.В. 20

Семафоры

• Изобретатель – Dijkstra (Нидерланды), в 1968 году.

• Более высокий уровень абстракции, чем защёлки

• Семафор, это переменная, которой можно управлять с помощью двух операций:– P(sem) – ожидать, пока sem не станет больше 0,

затем уменьшить sem на 1 и продолжить

– V(sem) – увеличить sem на 1.

• P() и V()? P() это Wait(), а V() это Signal() ?!

© 2013 Брагин А.В. 21

Семафоры 2

• Всё объясняется происхождением происхождением Дейкстры

• Probeer (нидерл.) – пробовать

• Verhoog (нидерл.) – увеличивать

• Важно запомнить:

– Перед verhoog’ом нужно probeer’овать! И наоборот.

© 2013 Брагин А.В. 22

Семафоры 3

• У каждого семафора есть ассоциированная с ним очередь потоков

• Когда поток вызывает P(sem)– Если семафор доступен (т.е. > 0), то уменьшить sem и

вернуть управление потоку– Если семафор недоступен (0), то поместить поток в

соответствующую очередь, и дать управление другому потоку

• Когда поток вызывает V(sem)– Если есть потоки в очереди (ожидающие семафора),

разблокировать один из них. Если это тот же, который вызвал V – просто передать ему управление

– Если нет ожидающих, то просто увеличить sem на 1

© 2013 Брагин А.В. 23

Два типа семафоров

• Бинарный (мьютексный) семафор– sem исходно = 1

– Гарантирует взаимноисключающий доступ к ресурсу (напр., критической секции кода)

– Только один поток/процесс может захватить ресурс

– Логически эквивалентен защёлке с блокировкой, а не спинлоку

• Считающий семафор– Исходно sem равно N, где N – число единиц ресурса

(или потоков, которые могут использовать эти ресурсы)

– Одновременно до N поток могут захватывать ресурс

© 2013 Брагин А.В. 24

Проблемы

• Семафоры, блокировки и условные переменные – какие проблемы?

• Они могут быть использованы для решения традиционных задач синхронизации, но легко совершить ошибки– По-сути это глобальные переменные с общим доступом– Нет связи между этими переменными и данными, доступом

к которым они управляют– Нет контроля за их использованием

• Условные переменные – будет ли сигнал вообще?• Семафоры – будет ли V()?• Защёлки – был ли вызван Lock() в нужное время. А Unlock()? Или

вообще ничего не было вызвано?

• Поэтому, появляются ошибки. Нужна поддержка в языке.

© 2013 Брагин А.В. 25

Монитор

• Ещё один подход к синхронизации• Монитор – это конструкция языка программирования, которая

поддерживает контролируемый доступ к общим данным• Код синхронизации добавляется компилятором• Монитор – это класс, в котором каждый метод автоматически

выполняет Lock() при входе в метод, и Unlock() при выходе из него• Поэтому он объединяет

– Общие структуры данных– Процедуры, работающие над этими данными (методы объекта)– Синхронизацию между потоками, вызывающими эти процедуры

• Доступ к данным может быть только из монитора, используя его процедуры– Защита доступа– Точное понимание какие данные защищены монитором

• Решает ключевые проблемы семафоров

© 2013 Брагин А.В. 26

Монитор 2

© 2013 Брагин А.В. 27

Общие данные

Очередь ожидания потоков,

пытающихся войти в монитор

операции (методы)В один момент

времени в

мониторе только

один поток

Процедура A

Процедура B

Процедура C

Этот

прямоугольник

не обязательно

один процесс!

Рисунок из лекций Gribble, Lazowska, Levy, Zahorjan, университет Washington, США.

Мониторы и Java

• Java предоставляет синхронизацию, схожую с монитором. Но в полной мере это не мониторы.

• У каждого Java объекта есть защёлка

• Ключевое слово synchronized захватывает эту защёлку

• Может применяться к отдельным методам или целым блокам кода

© 2013 Брагин А.В. 28

Тупиковая ситуация

• Или «взаимная блокировка». Одно английское слово - deadlock

• Поток находится в тупике, если он ожидает события, которое никогда не произойдёт

• Пример:

– Поток А в КС 1, ожидает доступа к КС 2; Поток Б в КС 2, ожидает доступа к КС 1

© 2013 Брагин А.В. 29

Четыре условия тупика

1. Взаимное исключение

2. Взять и ждать

3. Нет вытеснения

4. Круговое ожидание

• Соответственно, нарушив одно из этих условий можно избежать тупика

© 2013 Брагин А.В. 30

Граф ресурсов

• Граф распределения ресурсов (граф ожидания) –это способ визуализации состояния потоков для определения ситуаций взаимной блокировки

• Две доли графа– Вершины T соответствуют потокам или транзакциям

(напр. в случае СУБД)

– Вершины R соответствуют ресурсам, которые могут быть захвачены потоками

• Дуги– От ресурса к потоку – поток владеет ресурсом

– От потока к ресурсу – поток освобождает ресурс

© 2013 Брагин А.В. 31

Взаимная блокировка на графе

• Взаимная блокировка в том случае, если в графе есть нередуцируемые циклы

© 2013 Брагин А.В. 32

T1 T2

R2

R1R1 захвачен потоком

Поток ожидает R1

R2 захвачен потоком

Поток ожидает R2

Обработка тупиковых ситуаций

• Либо исключить одно из четырёх условий тупика– Взаимное исключение – это-то точно нельзя убрать– Удержание и ожидание– Отсутствие принудительного освобождения

ресурсов– Циклическое ожидание

• Обработку можно классифицировать на– Предотвращение тупиков– Избежание тупиков– Обнаружение и восстановление

© 2013 Брагин А.В. 33

Предотвращение

• Программы должны придерживаться строгих правил для того, чтобы гарантированно избежать взаимной блокировки

• Устранение «удержания и ожидания»– Каждый поток вначале получает все ресурсы

– Заблокирован пока все ресурсы не освободятся

• Устранение циклического ожидания– Ресурсы пронумерованы

– Каждый поток захватывает ресурс в порядке номеров. Если ему нужно захватить ресурс № k, то даже если ему ненужны ресурсы [0,k) он их всё равно захватит

© 2013 Брагин А.В. 34

Избежание

• Менее строгие ограничения

• Устранение циклического ожидания

– Каждый поток устанавливает свою максимальную потребность для каждого типа ресурсов

– При каждом запросе о выделении ресурса система выполняет алгоритм банкира

© 2013 Брагин А.В. 35

Обнаружение и восстановление

• В некоторый момент времени проверять, есть ли в системе тупиковая ситуация

• Если есть, то предпринять действия к разрешению тупиковой ситуации

© 2013 Брагин А.В. 36

Практика в СУБД

• Microsoft SQL Server. Автоматически обнаруживает ситуации взаимной блокировки. Ядро СУБД выбирает одну из заблокированных сессий в качестве «жертвы» и выполняет принудительное её завершение с ошибкой

• Oracle. То же, что и MSSQL, но с дополнительными «наворотами».

© 2013 Брагин А.В. 37

Практика в ОС

• Применительно к Windows (и Linux тоже)– Многопоточное реентерабельное ядро

предоставляет большие возможности взаимной блокировки…

– Оригинально, в ранних версиях NT были предусмотрены многоуровневые мьютексы, от которых впоследствии были вынуждены отказаться….

– TL;DR: В ядрах ОС есть обнаружение взаимных блокировок. Фундаментально проблема избежания тупиков в ядрах ОС на практике не решается.

© 2013 Брагин А.В. 38