Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

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

679 views

Published on

Синхронизация в ОС

Видео можно посмотреть на канале http://www.youtube.com/playlist?list=PLjSDyY6BQPVe2Zhxew5rJy2S-2_9t1vvn

Published in: Education
  • Login to see the comments

  • Be the first to like this

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

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

×