Mais conteúdo relacionado
Semelhante a Операционные системы 2015, лекция № 5 (20)
Mais de Aleksey Bragin (8)
Операционные системы 2015, лекция № 5
- 2. Критические секции
• Последовательность инструкций,
одновременное выполнение которой может
привести к неправильным результатам
называется критической секцией
• Т.о. взаимное исключение (mutual exclusion)
между двумя критическими секциями
достаточно для корректного исполнения
• Один из способов гарантировать взаимное
исключение – использовать блокировки (locks)
© 2013 Брагин А.В. 2
- 3. Пример КС
© 2013 Брагин А.В. 3
Поток1 Поток2 Поток1 Поток2 Поток1 Поток2
Некорректно Правильно Правильно
- 4. Критические секции 2
• В каких случаях возникают критические
секции?
– В одновременно выполняемом коде
– Действия прочитать-изменить-записать
– Общая переменная
• Общие переменные
– Глобальные и выделенные из кучи переменные
– Любые другие не локальные переменные в
стэке
© 2013 Брагин А.В. 4
- 5. Классический пример
• Функция снятия денег со счёта фирмы в банке
(использовать только для своих счетов!)
• Предположим, что одновременно происходит
выплата заработной платы 10000руб. с одного
счёта двум сотрудникам. А на счету находится
100 000 руб.
© 2013 Брагин А.В. 5
void WithdrawMoney(account, amount) {
int balance = GetBalance(account); // чтение
balance -= amount; // изменение
SetBalance(account, balance); // запись
GiveCashToUser();
}
- 6. Классический пример 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();
}
- 7. Классический пример 3
• Если многозадачность вытесняющая, то
переключение контекста может произойти
в любом месте
• Гарантировать, что три операции пройдут в
нужной последовательности нельзя
• Даже такой простой код, как k++ не
является безопасным! (если k – общая
переменная)
© 2013 Брагин А.В. 7
- 8. Требования к критическим секциям
• Каким требованиям должна удовлетворять
правильная реализация критических секций
– Взаимное исключение
• Максимум один поток может быть в критической секции
• Другой поток вне критической секции не может
вытеснить поток, находящийся в критической секции
– Ограниченное ожидание
• Если поток ожидает входа в критическую секцию, то рано
или поздно это произойдёт
– Производительность
• Накладные расходы на вход и выход из критических
секций малы по сравнению с работой, выполняемой
внутри критических секций
© 2013 Брагин А.В. 8
- 9. Механизмы реализации КС
• Запрет прерываний
– Самый простой, но очень много недостатков
• Алгоритм Петерсона
• Спинлоки (spinlocks)
– Базовый примитив, часто используется для построения более сложных
блокировок
• Семафоры (non-spinning locks)
– Просты для понимания, но сложны в использовании
• Мониторы
– Высокоуровневая блокировка, требует поддержки со стороны языка
программирования
– Легче использовать
• Сообщения
– Простая модель взаимодействия и синхронизации на основе
передаваемых сообщений через канал
– Используется преимущественно в распределённых системах
© 2013 Брагин А.В. 9
- 10. Запрет прерываний
• Простейший подход: временно отключить
прерывания
• Недостатки:
– Доступно только ядру, режим пользователя не
может отключать прерывания
– Недостаточно на многопроцессорной системе (у
каждого ЦП – свои прерывания)
– Длительный период отключения прерываний
может привести к неработоспособности устройств
• Используется только в редких случаях
© 2013 Брагин А.В. 10
- 11. Алгоритм Петерсона
• Работает для ограниченного числа процессов 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; // Сообщаем о выходе из КС
}
- 12. Блокировки/защёлки (locks)
• Защёлка – это объект с двумя операциями
– Lock(): т.е. войти в крит. секцию
– Unlock(): выйти из крит. секции
• Lock() блокирует дальнейшее выполнение
потока до тех пор, пока не будет выполнен
вход в крит. секцию
• Можно сказать, что поток держит эту
защёлку, а потом освобождает её.
© 2013 Брагин А.В. 12
- 14. Пример с банком и защёлкой
© 2013 Брагин А.В. 14
void WithdrawMoney(account, amount) {
Lock(globalLock);
int balance = GetBalance(account); // чтение
balance -= amount; // изменение
SetBalance(account, balance); // запись
Unlock(globalLock);
GiveCashToUser();
}
- 15. Спинлок
• Циклическая блокировка, необходима
аппаратная реализация
• 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;
}
- 16. Реализация спинлока
• Внутри спинлока есть критическая секция
• Блокировка/разблокировка должна быть
атомарной операцией
– Атомарная – выполняется без прерывания,
либо всё, либо ничего
• Поэтому нужна аппаратная поддержка
– Специальные инструкции: test-and-set,
compare-and-swap
– Временное отключение прерываний
© 2013 Брагин А.В. 16
- 17. Аппаратный test-and-set
• Внутри ЦП реализована следующая
атомарная операция:
© 2013 Брагин А.В. 17
int TestAndSet(int *Val) {
int OldValue = *Val;
*Val = 1;
return OldValue;
}
- 18. Реализация спинлока 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;
}
- 19. Проблемы спинлока
• Неэффективны: если поток заблокирован
спинлоком, то будет находиться в цикле до
окончания кванта времени
• Спинлоки – примитивы для более
высокоуровневой синхронизации
© 2013 Брагин А.В. 19
- 21. Семафоры
• Изобретатель – Dijkstra (Нидерланды), в 1968
году.
• Более высокий уровень абстракции, чем
защёлки
• Семафор, это переменная, которой можно
управлять с помощью двух операций:
– P(sem) – ожидать, пока sem не станет больше 0,
затем уменьшить sem на 1 и продолжить
– V(sem) – увеличить sem на 1.
• P() и V()? P() это Wait(), а V() это Signal() ?!
© 2013 Брагин А.В. 21
- 22. Семафоры 2
• Всё объясняется происхождением
происхождением Дейкстры
• Probeer (нидерл.) – пробовать
• Verhoog (нидерл.) – увеличивать
• Важно запомнить:
– Перед verhoog’ом нужно probeer’овать! И
наоборот.
© 2013 Брагин А.В. 22
- 23. Семафоры 3
• У каждого семафора есть ассоциированная с ним
очередь потоков
• Когда поток вызывает P(sem)
– Если семафор доступен (т.е. > 0), то уменьшить sem и
вернуть управление потоку
– Если семафор недоступен (0), то поместить поток в
соответствующую очередь, и дать управление другому
потоку
• Когда поток вызывает V(sem)
– Если есть потоки в очереди (ожидающие семафора),
разблокировать один из них. Если это тот же, который
вызвал V – просто передать ему управление
– Если нет ожидающих, то просто увеличить sem на 1
© 2013 Брагин А.В. 23
- 24. Два типа семафоров
• Бинарный (мьютексный) семафор
– sem исходно = 1
– Гарантирует взаимноисключающий доступ к ресурсу
(напр., критической секции кода)
– Только один поток/процесс может захватить ресурс
– Логически эквивалентен защёлке с блокировкой, а не
спинлоку
• Считающий семафор
– Исходно sem равно N, где N – число единиц ресурса
(или потоков, которые могут использовать эти ресурсы)
– Одновременно до N поток могут захватывать ресурс
© 2013 Брагин А.В. 24
- 25. Проблемы
• Семафоры, блокировки и условные переменные – какие
проблемы?
• Они могут быть использованы для решения
традиционных задач синхронизации, но легко
совершить ошибки
– По-сути это глобальные переменные с общим доступом
– Нет связи между этими переменными и данными, доступом
к которым они управляют
– Нет контроля за их использованием
• Условные переменные – будет ли сигнал вообще?
• Семафоры – будет ли V()?
• Защёлки – был ли вызван Lock() в нужное время. А Unlock()? Или
вообще ничего не было вызвано?
• Поэтому, появляются ошибки. Нужна поддержка в
языке.
© 2013 Брагин А.В. 25
- 26. Монитор
• Ещё один подход к синхронизации
• Монитор – это конструкция языка программирования, которая
поддерживает контролируемый доступ к общим данным
• Код синхронизации добавляется компилятором
• Монитор – это класс, в котором каждый метод автоматически
выполняет Lock() при входе в метод, и Unlock() при выходе из него
• Поэтому он объединяет
– Общие структуры данных
– Процедуры, работающие над этими данными (методы объекта)
– Синхронизацию между потоками, вызывающими эти процедуры
• Доступ к данным может быть только из монитора, используя его
процедуры
– Защита доступа
– Точное понимание какие данные защищены монитором
• Решает ключевые проблемы семафоров
© 2013 Брагин А.В. 26
- 27. Монитор 2
© 2013 Брагин А.В. 27
Общие данные
Очередь ожидания потоков,
пытающихся войти в монитор
операции (методы)В один момент
времени в
мониторе только
один поток
Процедура A
Процедура B
Процедура C
Этот
прямоугольник
не обязательно
один процесс!
Рисунок из лекций Gribble, Lazowska, Levy, Zahorjan,
университет Washington, США.
- 28. Мониторы и Java
• Java предоставляет синхронизацию, схожую
с монитором. Но в полной мере это не
мониторы.
• У каждого Java объекта есть защёлка
• Ключевое слово synchronized захватывает
эту защёлку
• Может применяться к отдельным методам
или целым блокам кода
© 2013 Брагин А.В. 28
- 29. Тупиковая ситуация
• Или «взаимная блокировка». Одно
английское слово - deadlock
• Поток находится в тупике, если он ожидает
события, которое никогда не произойдёт
• Пример:
– Поток А в КС 1, ожидает доступа к КС 2; Поток Б
в КС 2, ожидает доступа к КС 1
© 2013 Брагин А.В. 29
- 30. Четыре условия тупика
1. Взаимное исключение
2. Взять и ждать
3. Нет вытеснения
4. Круговое ожидание
• Соответственно, нарушив одно из этих
условий можно избежать тупика
© 2013 Брагин А.В. 30
- 31. Граф ресурсов
• Граф распределения ресурсов (граф ожидания) –
это способ визуализации состояния потоков для
определения ситуаций взаимной блокировки
• Две доли графа
– Вершины T соответствуют потокам или транзакциям
(напр. в случае СУБД)
– Вершины R соответствуют ресурсам, которые могут
быть захвачены потоками
• Дуги
– От ресурса к потоку – поток владеет ресурсом
– От потока к ресурсу – поток освобождает ресурс
© 2013 Брагин А.В. 31
- 32. Взаимная блокировка на графе
• Взаимная блокировка в том случае, если в
графе есть нередуцируемые циклы
© 2013 Брагин А.В. 32
T1 T2
R2
R1
R1 захвачен потоком
Поток ожидает R1
R2 захвачен потоком
Поток ожидает R2
- 33. Обработка тупиковых ситуаций
• Либо исключить одно из четырёх условий
тупика
– Взаимное исключение – это-то точно нельзя убрать
– Удержание и ожидание
– Отсутствие принудительного освобождения
ресурсов
– Циклическое ожидание
• Обработку можно классифицировать на
– Предотвращение тупиков
– Избежание тупиков
– Обнаружение и восстановление
© 2013 Брагин А.В. 33
- 34. Предотвращение
• Программы должны придерживаться строгих
правил для того, чтобы гарантированно избежать
взаимной блокировки
• Устранение «удержания и ожидания»
– Каждый поток вначале получает все ресурсы
– Заблокирован пока все ресурсы не освободятся
• Устранение циклического ожидания
– Ресурсы пронумерованы
– Каждый поток захватывает ресурс в порядке номеров.
Если ему нужно захватить ресурс № k, то даже если ему
ненужны ресурсы [0,k) он их всё равно захватит
© 2013 Брагин А.В. 34
- 35. Избежание
• Менее строгие ограничения
• Устранение циклического ожидания
– Каждый поток устанавливает свою
максимальную потребность для каждого типа
ресурсов
– При каждом запросе о выделении ресурса
система выполняет алгоритм банкира
© 2013 Брагин А.В. 35
- 36. Обнаружение и восстановление
• В некоторый момент времени проверять,
есть ли в системе тупиковая ситуация
• Если есть, то предпринять действия к
разрешению тупиковой ситуации
© 2013 Брагин А.В. 36
- 37. Практика в СУБД
• Microsoft SQL Server. Автоматически
обнаруживает ситуации взаимной
блокировки. Ядро СУБД выбирает одну из
заблокированных сессий в качестве
«жертвы» и выполняет принудительное её
завершение с ошибкой
• Oracle. То же, что и MSSQL, но с
дополнительными «наворотами».
© 2013 Брагин А.В. 37
- 38. Практика в ОС
• Применительно к Windows (и Linux тоже)
– Многопоточное реентерабельное ядро
предоставляет большие возможности взаимной
блокировки…
– Оригинально, в ранних версиях NT были
предусмотрены многоуровневые мьютексы, от
которых впоследствии были вынуждены
отказаться….
– TL;DR: В ядрах ОС есть обнаружение взаимных
блокировок. Фундаментально проблема
избежания тупиков в ядрах ОС на практике не
решается.
© 2013 Брагин А.В. 38