← Назад к вопросам

На чем низкоуровнево построены локи?

2.7 Senior🔥 12 комментариев
#Многопоточность и асинхронность

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Низкоуровневая реализация блокировок (локов) в iOS/macOS

В основе блокировок в iOS/macOS лежат примитивы синхронизации, предоставляемые операционной системой и аппаратным обеспечением. Реализация зависит от конкретного типа лока и оптимизаций, но в целом строится на нескольких ключевых уровнях.

1. Аппаратный уровень: атомарные операции

Самый фундаментальный слой — атомарные инструкции процессора, которые гарантируют неделимость операций в многопроцессорной среде. На архитектуре ARM (iOS) это инструкции типа:

LDREX   ; Load Exclusive (эксклюзивная загрузка)
STREX   ; Store Exclusive (эксклюзивная запись)

Эти инструкции реализуют механизм LL/SC (Load-Link/Store-Conditional), который позволяет безопасно изменять значения в памяти в многопоточной среде. Пример спин-лока на атомарных операциях:

bool atomic_compare_and_swap(volatile int* ptr, int expected, int new) {
    // Псевдокод для CAS операции
    if (*ptr == expected) {
        *ptr = new;
        return true;
    }
    return false;
}

2. Уровень ядра: системные вызовы

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

  • pthread_mutex в iOS/macOS использует Mach-примитивы (ядро Darwin)
  • os_unfair_lock — легковесная блокировка с минимальным временем переключения в ядро
  • libkern/OSAtomic — устаревший API для атомарных операций

3. Основные типы локов и их реализация

Spinlocks (спин-локи)

Изначально реализовались через OSSpinLock, но из-за проблем с priority inversion на iOS теперь не рекомендуется. Принцип работы:

while (!atomic_cas(&lock, 0, 1)) {
    // Активное ожидание (busy-waiting)
}

Unfair Locks (os_unfair_lock)

Замена спин-локов в современных системах:

os_unfair_lock_t lock = &OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(lock);
// Критическая секция
os_unfair_lock_unlock(lock);

Особенности:

  • Минимальный оверхед при переключении в ядро
  • Агрессивная политика — может привести к "голоданию" потоков
  • Не является рекурсивным

Mutex (pthread_mutex_t)

Стандартный мьютекс POSIX с поддержкой рекурсивности и атрибутов:

pthread_mutex_t mutex;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex, &attr);

Внутренняя структура включает:

  • Состояние блокировки (занята/свободна)
  • Очередь ожидающих потоков
  • Счетчик рекурсивных захватов
  • Тип мьютекса (нормальный/рекурсивный/ошибко-проверяющий)

Read-Write Locks (pthread_rwlock_t)

pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
pthread_rwlock_rdlock(&rwlock);    // Блокировка на чтение
pthread_rwlock_wrlock(&rwlock);    // Блокировка на запись

4. Механизмы ожидания и уведомления

Для эффективного управления потоками используются:

  • Futex (Fast Userspace Mutex) — Linux-аналог (в Darwin есть схожие механизмы)
  • Mach semaphores — низкоуровневые семафоры ядра Mach
  • Condition Variables (pthread_cond_t) в комбинации с мьютексами

Пример condition variable:

pthread_mutex_t mutex;
pthread_cond_t cond;
bool ready = false;

// Поток-ожидающий
pthread_mutex_lock(&mutex);
while (!ready) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);

// Поток-сигналящий
pthread_mutex_lock(&mutex);
ready = true;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);

5. Оптимизации и особенности iOS/macOS

Quality of Service (QoS) и priority inversion avoidance

Система отслеживает инверсию приоритетов и автоматически ее устраняет через:

  • Priority inheritance protocol — временное повышение приоритета
  • Priority ceiling protocol — установка максимального приоритета

Hybrid подходы

Современные локи часто используют адаптивные стратегии:

  1. Сначала активное ожидание (spin)
  2. При длительном ожидании — переход в режим сна
  3. Использование handoff lock для передачи блокировки

Атомарные операции на уровне компилятора

// Современные atomic операции
#include <stdatomic.h>
atomic_int counter = ATOMIC_VAR_INIT(0);
atomic_fetch_add_explicit(&counter, 1, memory_order_acq_rel);

6. Особенности Swift/Objective-C блокировок

@synchronized в Objective-C — высокоуровневая обертка, которая:

  • Использует рекурсивный мьютекс
  • Основана на ассоциативном хранении lock-объектов
  • Имеет значительный оверхед

NSLock, NSRecursiveLock, NSCondition — Foundation-обертки над pthread примитивами с добавлением Objective-C семантики.

7. Memory Barriers и порядок операций

При реализации локов критически важны барьеры памяти:

  • acquire barrier — гарантирует, что операции после барьера не будут переупорядочены до него
  • release barrier — гарантирует, что операции до барьера не будут переупорядочены после
// Пример использования барьеров
void lock(atomic_flag *lock) {
    while (atomic_flag_test_and_set_explicit(lock, memory_order_acquire)) {
        // spin
    }
}

void unlock(atomic_flag *lock) {
    atomic_flag_clear_explicit(lock, memory_order_release);
}

Заключение

Низкоуровневая реализация блокировок в iOS представляет собой многоуровневую систему, начиная от аппаратных атомарных операций процессора ARM, через примитивы ядра Mach, до высокоуровневых API в POSIX и Foundation. Современные реализации делают акцент на эффективности, предотвращении инверсии приоритетов и адаптивном поведении в зависимости от нагрузки. Понимание этих механизмов необходимо для написания корректного многопоточного кода и диагностики проблем синхронизации.

На чем низкоуровнево построены локи? | PrepBro