Upload
aleksey-bragin
View
85
Download
3
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 Брагин А.В. 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