Previous slide Next slide Toggle fullscreen Toggle overview view Open presenter view
Системное программирование
Тема 4. Организация параллельной обработки с использованием средств исключения и предупреждения состязаний
Системное программирование
План лекции
Учебные вопросы
Параллелизм, разделяемая память и критерии Дейкстры
Алгоритм Петерсона, Out-of-Order и барьеры памяти
Аппаратные атомики (CAS, LOCK), проблема ABA
Легковесные блокировки (SRWLock, futex, спинлоки)
Задача «Производители–Потребители» и дедлоки (Deadlock)
Цели занятия
Понять влияние внеочередного исполнения ЦПУ на память
Изучить работу инструкции Compare-And-Swap (CAS)
Освоить применение легковесных блокировок Ring 3
Реализовать схему «Производитель–Потребитель»
Изучить 4 условия Коффмана и иерархический порядок захвата
Организация параллельной обработки и средств исключения
Системное программирование
Параллельная обработка и разделяемые ресурсы
Параллелизм в архитектурах SMP
Многоядерные процессоры исполняют потоки одновременно
Потоки разделяют память: кучу (Heap), глобальные переменные, дескрипторы
Обеспечивает масштабирование вычислительной мощности
Проблема состязаний (Race Condition)
Недетерминированное поведение при одновременной модификации
Искажение данных: считывание частично записанных структур
Требует строгой изоляции критических участков программы
Организация параллельной обработки и средств исключения
Системное программирование
Проблема состязаний и условия Дейкстры
Понятие критической секции
Критическая секция: участок программного кода, обращающийся к разделяемому ресурсу
Должен выполняться как атомарная транзакция
Три условия Дейкстры (1965)
Взаимное исключение: внутри критической секции одновременно не более одного потока
Прогресс: свободная секция немедленно доступна любому ждущему потоку
Ограниченное ожидание: конечное время ожидания; отсутствие голодания (Starvation)
Организация параллельной обработки и средств исключения
Системное программирование
Алгоритм Петерсона для двух потоков
Программная синхронизация (1981)
bool flag[2 ] = {false , false };
int turn = 0 ;
void Enter_Peterson (void ) {
flag[0 ] = true ;
turn = 1 ;
while (flag[1 ] && turn == 1 ) {
}
}
void Leave_Peterson (void ) {
flag[0 ] = false ;
}
Доказательство корректности
turn равен либо 0, либо 1, и не может быть равен обоим одновременно
Поток, записавший turn последним, сам себя блокирует в while
Обеспечивает взаимное исключение и прогресс на модели последовательной консистентности
Организация параллельной обработки и средств исключения
Системное программирование
Out-of-Order исполнение и барьеры памяти
Аппаратное переупорядочивание
Современные процессоры оптимизируют конвейер через Store Buffers
Чтение обгоняет запись: Store-Load Reordering
Внеочередное исполнение (Out-of-Order) приводит к тому, что оба потока входят в секцию Петерсона!
Барьеры памяти (Memory Fences)
MFENCE (x86/x64): останавливает конвейер до сброса буферов записи в кэш L1
Запрещают переупорядочивание инструкций компилятором и микропроцессором
В C11/C++20: std::memory_order_seq_cst, acquire, release
Организация параллельной обработки и средств исключения
Системное программирование
Программное взаимное исключение и аппаратные атомики
Организация параллельной обработки и средств исключения
Системное программирование
Аппаратная атомарность: Compare-And-Swap (CAS)
Семантика операции CAS
bool CAS (int *addr, int exp , int val) {
ATOMICALLY {
if (*addr == exp ) {
*addr = val;
return true ;
}
return false ;
}
}
Основа неблокирующих алгоритмов (Lock-Free)
Позволяет безопасно обновить указатель без захвата мьютекса
Инструкция ЦПУ: LOCK CMPXCHG
Выполняет чтение, сравнение и запись неделимо
Блокирует кэш-линию в монопольном состоянии протокола MESI (Modified)
В Windows: InterlockedCompareExchange
В Linux / GCC: __atomic_compare_exchange_n
Организация параллельной обработки и средств исключения
Системное программирование
Проблема ABA в Lock-Free структурах
Сущность проблемы ABA
Поток 1 считывает вершину стека со значением A
Поток 1 вытеснен планировщиком
Поток 2 удаляет A, удаляет B, освобождает память
Поток 3 выделяет узел; аллокатор возвращает тот же адрес A!
Поток 1 просыпается: *top == A (успех CAS), но A->next указывает на разрушенный узел B
Результат: разрушение структуры данных!
Архитектурное решение
Версионирование (Tagged Pointers): вместе с адресом хранится 64-битный счетчик изменений
При каждом изменении счетчик версий инкрементируется: (A, 1) -> (B, 2) -> (A, 3)
В x86-64: 128-битная инструкция LOCK CMPXCHG16B
Организация параллельной обработки и средств исключения
Системное программирование
Иерархия и накладные расходы синхронизации
Организация параллельной обработки и средств исключения
Системное программирование
Примитивы пользовательского режима: Спинлоки и SRWLock
Спинлоки (Spinlocks)
Активное ожидание в цикле:
while (__atomic_test_and_set(&lock,
__ATOMIC_ACQUIRE)) {
_mm_pause();
}
Сверхмалая задержка (~10–20 нс)
Сжигает 100% ядра ЦПУ при долгом локе
Блокировки SRWLock (WinAPI)
Занимает размер указателя (8 байт)
Читатели (AcquireSRWLockShared) и писатели (AcquireSRWLockExclusive)
Без состязания: захват за 1 атомарную инструкцию
Без создания дескрипторов ядра
Организация параллельной обработки и средств исключения
Системное программирование
Двухфазные гибридные блокировки: CRITICAL_SECTION и Futex
CRITICAL_SECTION в Windows
Совмещает фазу спина и сон ядра:
Фаза спина (Spin Phase): крутится в Ring 3 до N N N циклов (SetCriticalSectionSpinCount)
Фаза ядра (Sleep Phase): при неудаче усыпляет поток в очереди ядра
Оптимальный баланс между задержкой и энергопотреблением
Механизм futex в Linux
Fast Userspace Mutex: целое число int
Захват в Ring 3 через atomic_cmpxchg (0 системных вызовов)
Системный вызов sys_futex(FUTEX_WAIT) инициируется только при реальном конфликте
Организация параллельной обработки и средств исключения
Системное программирование
Условные переменные (Condition Variables)
Назначение и функции
Ожидание выполнения логического предиката над разделяемыми данными
В Windows: CONDITION_VARIABLE, SleepConditionVariableSRW, WakeConditionVariable
В Linux / POSIX: pthread_cond_t, pthread_cond_wait, pthread_cond_signal
Канонический шаблон ожидания
AcquireSRWLockExclusive(&lock);
while (!predicate_is_true()) {
SleepConditionVariableSRW(&cv,
&lock, INFINITE, 0 );
}
ReleaseSRWLockExclusive(&lock);
Защита от ложных пробуждений (Spurious Wakeups): проверка условия строго в while
Организация параллельной обработки и средств исключения
Системное программирование
Задача «Производители–Потребители» (Bounded Buffer)
Организация параллельной обработки и средств исключения
Системное программирование
Реализация схемы «Производитель–Потребитель»
Код производителя (Produce)
WaitForSingleObject(b->sem_empty, INFINITE);
EnterCriticalSection(&b->cs);
b->data[b->tail] = item;
b->tail = (b->tail + 1 ) % BUF_SIZE;
LeaveCriticalSection(&b->cs);
ReleaseSemaphore(b->sem_full, 1 , NULL );
Код потребителя (Consume)
WaitForSingleObject(b->sem_full, INFINITE);
EnterCriticalSection(&b->cs);
int item = b->data[b->head];
b->head = (b->head + 1 ) % BUF_SIZE;
LeaveCriticalSection(&b->cs);
ReleaseSemaphore(b->sem_empty, 1 , NULL );
Организация параллельной обработки и средств исключения
Системное программирование
Взаимоблокировки (Deadlock) и 4 условия Коффмана
Понятие Deadlock
Аварийная остановка системы, при которой потоки бесконечно ожидают ресурсы друг друга
Потоки не потребляют такты ЦПУ, но приложение полностью зависает
4 необходимых условия Коффмана (1971)
Взаимное исключение: ресурсы неделимы и монопольны
Удержание и ожидание: поток удерживает ресурс и запрашивает новый
Невытесняемость: ресурс нельзя отобрать принудительно
Круговое ожидание: замкнутая цепочка ожидания { T 1 → T 2 → . . . → T 1 } \{T_1 \to T_2 \to ... \to T_1\} { T 1 → T 2 → ... → T 1 }
Организация параллельной обработки и средств исключения
Системное программирование
Граф распределения ресурсов и цикл дедлока
Организация параллельной обработки и средств исключения
Системное программирование
Стратегии предотвращения и устранения дедлоков
Иерархическое упорядочение (Lock Ordering)
Все ресурсы нумеруются: R 1 , R 2 , . . . , R n R_1, R_2, ..., R_n R 1 , R 2 , ... , R n
Потоки обязаны захватывать ресурсы строго по возрастанию номеров
Разрушает 4-е условие Коффмана (круговой цикл математически невозможен)
Дополнительные методы
Неблокирующий захват (try_lock): при неудаче освобождает ранее захваченные ресурсы и делает откат
Алгоритм банкира Дейкстры: планировщик выделяет ресурс, только если состояние системы безопасно
Сторожевой таймер (Watchdog): обнаружение циклов и принудительный откат одного из потоков
Организация параллельной обработки и средств исключения
Системное программирование
Задача об обедающих философах (Dining Philosophers)
Постановка проблемы
5 философов сидят за круглым столом, между ними 5 вилок
Для еды каждому требуются две вилки (левая и правая)
Дедлок: если все одновременно возьмут левую вилку, ни один не получит правую — гибель от голода
Инженерные решения
Асимметричный порядок (Lock Ordering):
Четные философы берут сначала левую вилку, затем правую
Нечетные философы берут сначала правую, затем левую
Разрывает замкнутый цикл ожидания
Ограничение доступа (Семафор):
За стол допускаются не более 4 философов одновременно
Организация параллельной обработки и средств исключения
Системное программирование
Резюме лекции
Аппаратура и атомики
Несогласованный доступ к разделяемой памяти ведет к Race Condition
Out-of-Order конвейер процессора требует барьеров памяти (MFENCE)
LOCK CMPXCHG (CAS) выполняет атомарный обмен на уровне кэшей MESI
Проблема ABA решается версионированием указателей (Tagged Pointers)
Примитивы и дедлоки
SRWLock и спинлоки минимизируют накладные расходы в Ring 3
CRITICAL_SECTION и futex используют гибридную схему (spin + sleep)
«Производители–потребители» требуют семафоров и условия порядка
Дедлок предотвращается иерархическим упорядочением захвата (Lock Ordering)
Организация параллельной обработки и средств исключения
Системное программирование
Вопросы для самопроверки
Вопросы 1–5
Сформулируйте три фундаментальных критерия Дейкстры для критических секций.
Почему алгоритм Петерсона не работает на современных многоядерных процессорах без MFENCE?
В чем заключается логика инструкции Compare-And-Swap (CAS) и как обеспечивается её неделимость?
Что такое проблема ABA в Lock-Free структурах и как она устраняется?
В чем различие между спинлоком и примитивом SRWLock по нагрузке на ЦПУ?
Вопросы 6–10
Как работает двухфазный алгоритм захвата в объекте CRITICAL_SECTION?
Почему проверка предиката условной переменной обязана выполняться в цикле while?
Перечислите четыре необходимых условия Коффмана для возникновения дедлока.
Каким образом иерархический порядок захвата блокировок предотвращает взаимные блокировки?
Как решается проблема дедлока в задаче об обедающих философах через асимметричный захват вилок?
Организация параллельной обработки и средств исключения
Системное программирование
Литература и рекомендуемые ресурсы
Учебная литература
Современные операционные системы (4-е изд.) — Таненбаум Э., Бос Х. СПб.: Питер, 2021. 1119 с.
Операционные системы (2-е изд.) — Гордеев А. В. СПб.: Питер, 2009. 415 с.
Устройство и функционирование OC Windows — Коньков К. А. М.: Бином, 2008. 208 с.
Параллельное программирование на C++ в действии — Уильямс Э. М.: ДМК Пресс, 2021. 672 с.
Первоисточники и документация
Организация параллельной обработки и средств исключения
Лекция №4.
Цель: сформировать системное понимание архитектуры параллельной обработки данных, аппаратных и программных примитивов взаимного исключения, барьеров памяти, гибридных блокировок, классических задач синхронизации («производители-потребители», «обедающие философы») и условий предотвращения взаимоблокировок (Deadlock).
Связь с предыдущими темами: опирается на Тему 2 (процессы и потоки) и Тему 3 (объекты ядра и примитивы синхронизации).
Связь с практической работой: теоретическая основа для лабораторной работы №3 "Программирование многопоточных приложений".
План связывает низкоуровневые свойства аппаратуры ЦПУ с прикладными паттернами многопоточности.
Подчеркнуть: параллелизм на общих ресурсах невозможен без детерминированной синхронизации.
Критерии Дейкстры — теоретический стандарт проверки надежности любого синхронизирующего примитива.
Алгоритм Петерсона решает задачу взаимного исключения чисто программно на двух переменных.
Без барьеров памяти наивные программные алгоритмы синхронизации на современных CPU не работают.
Слева — алгоритм Петерсона с барьерами, справа — аппаратная семантика инструкции CAS.
Инструкция CMPXCHG с префиксом LOCK выполняется аппаратно неделимо на уровне кэшей процессора.
Проблема ABA — критическая ошибка в Lock-Free алгоритмах, решаемая парой (указатель + счетчик версий).
Иерархия: от сверхбыстрых атомиков Ring 3 до тяжеловесных объектов ядра с задержкой в микросекунды.
Спинлоки допустимы только на ультракоротких критических секциях, SRWLock — стандарт для Windows.
Гибридные блокировки исключают системные вызовы в 95% случаев при низком состязании потоков.
Всегда использовать while вместо if из-за ложных пробуждений, допускаемых стандартами ОС.
Классическая модель Дейкстры: два счетных семафора емкости и один мьютекс защиты буфера.
Обратить внимание: сначала семафор, потом мьютекс. Обратный порядок гарантирует дедлок!
Если нарушить хотя бы одно из четырех условий Коффмана, дедлок математически невозможен.
Ориентированный цикл в графе распределения ресурсов RAG строго доказывает наличие Deadlock.
Lock Ordering — золотой стандарт проектирования сложных системных сервисов и СУБД.
Классическая демонстрация: асимметрия в правилах захвата ресурсов гарантирует прогресс системы.
Подвести итог: от микроархитектуры ядер до безопасных многопоточных архитектур.
Опросить слушателей по условиям Коффмана, проблеме ABA и алгоритму Петерсона.
Завершить лекцию, объявить тему следующего занятия (Тема 5. Механизм сообщений).