Linux
Предпосылки: аппаратное обеспечение (CPU, кеш, RAM, хранилище, DMA).
Одна программа на одном процессоре — простая задача: загрузить код в память и выполнять инструкции. Десятки программ на нескольких ядрах — задача другого порядка: кто-то должен изолировать их друг от друга, распределить процессорное время и память, предоставить единый интерфейс к сотням устройств. Операционная система (ОС) превращает набор транзисторов в платформу для программ. Ядро Linux изолирует процессы друг от друга, абстрагирует оборудование через файловые дескрипторы и системные вызовы, распределяет CPU и память между сотнями задач. Каждый механизм — ответ на конкретную проблему: виртуальная память защищает процессы от чужих данных, планировщик обеспечивает отзывчивость при фоновой нагрузке, механизмы взаимного исключения работают быстро без перехода в ядро при отсутствии конкуренции, сервер масштабируется на десятки тысяч соединений, отслеживая готовность дескрипторов вместо опроса каждого по очереди.
Порядок изучения
Серия организована как ядро курса и несколько углублений. Ядро — линейная последовательность, каждая заметка опирается на предыдущие. Углубления тоже выстроены в рекомендуемом порядке: конкурентность, системное программирование, ядро, инфраструктура, контейнеры. Каждый модуль опирается на разные части ядра курса, но последовательное прохождение позволяет накапливать контекст — например, системное программирование использует знания о синхронизации из конкурентности, а модуль ядра опирается на API из системного программирования.
Ядро курса
Фундаментальные абстракции ОС от первого системного вызова до прав доступа. Читать по порядку — каждый файл использует понятия из предыдущих.
- Что такое операционная система — три проблемы без ОС, абстракция, изоляция, разделение ресурсов
- Режимы CPU и системные вызовы — как программа просит ядро выполнить привилегированную операцию
- Процессы — как программы запускают другие программы и что происходит при завершении
- Потоки — когда процесс слишком дорог и чем потоки решают проблему
- Файловые дескрипторы — «всё — файл»: как единый интерфейс объединяет файлы, сокеты и каналы
- Виртуальная память — как ядро создаёт иллюзию непрерывной памяти для каждого процесса
- Файловые системы — как данные попадают на диск и почему запись может пережить сбой
- Планировщик — как ядро выбирает, какой поток выполнять, и при каких условиях прерывает текущий
- Права доступа и capabilities — как ядро контролирует доступ к ресурсам и зачем нужна гранулярность тоньше root/не-root
Углубления
Три модуля. Рекомендуемый порядок: конкурентность, затем системное программирование, затем ядро. Конкурентность и системное программирование зависят от ядра курса, а модуль ядра дополнительно опирается на системное программирование.
Конкурентность
Предпосылки: потоки, виртуальная память.
Синхронизация потоков и модель памяти.
- Синхронизация — что ломается при одновременном доступе и какие примитивы это предотвращают
- Модель памяти — почему компилятор и процессор переупорядочивают операции с памятью и как это контролировать
- Lock-free структуры — параллельные структуры без блокировок: выигрыш, скрытые ловушки и когда они не нужны
Системное программирование
Предпосылки: ядро курса.
Практический API: сигналы, mmap, файловый ввод-вывод, сокеты, мультиплексирование.
- Сигналы — механизм уведомления процесса о событиях и почему обработка сигналов сложнее, чем кажется
- Отображение памяти — как отобразить файл или анонимную область в адресное пространство процесса
- Файловый ввод-вывод — гарантии записи, обход кеша и блокировка файлов
- Сокеты — создание соединений, локальные сокеты и передача данных без копирования
- Мультиплексирование ввода-вывода — как обрабатывать тысячи соединений одним потоком и почему каждое поколение API вытесняет предыдущее
- Управление памятью — управление физической памятью: большие страницы, переподписка и неоднородность
- Межпроцессное взаимодействие — способы обмена данными между процессами: от семафоров до передачи файловых дескрипторов
Ядро
Предпосылки: ядро курса, системное программирование.
Внутреннее устройство: как работают syscall, прерывания, драйверы, сетевой стек.
- Механизм системных вызовов — что происходит внутри ядра от инструкции syscall до возврата в пользовательский код
- Прерывания — как ядро обрабатывает аппаратные события, не блокируя остальную работу
- Устройства и драйверы — как ядро находит оборудование, загружает драйверы и предоставляет интерфейс через виртуальные файловые системы
- Сетевой стек — путь сетевого пакета от сетевой карты до приложения
- Управление памятью ядра — как ядро выделяет, возвращает и перераспределяет физическую память
Инфраструктура
Четыре заметки в рекомендуемом порядке. Каждая самодостаточна после ядра курса, но последовательное чтение даёт дополнительные связи: ELF-формат объясняет, откуда берутся адреса в виртуальной памяти; терминалы используют знания о дескрипторах и сессиях; трассировка опирается на системные вызовы и прерывания; загрузка системы завершает картину.
- ELF и линковка — как исполняемый файл попадает в память и находит библиотеки
- Терминалы — как терминал связывает клавиатуру с процессом и почему это сложнее, чем кажется
- Трассировка — как наблюдать за системой: от трассировки вызовов до профилирования
- Загрузка системы — цепочка загрузки: от нажатия кнопки питания до первого пользовательского процесса
Контейнеры
Синтез серии: namespaces, cgroups, overlay FS, seccomp, capabilities складываются в один docker run.
Предпосылки: ядро курса, системное программирование, ядро.
- Пространства имён и контрольные группы — как ядро создаёт изолированные миры внутри одной системы
- Контейнеры — как отдельные механизмы изоляции складываются в контейнер
Как всё связано
Изоляция vs производительность: создание процесса (fork) изолирует его полностью (~50 мкс), создание потока (clone) дешевле (~10 мкс), но потоки разделяют память и требуют синхронизации. Выбор определяет архитектуру: Redis — однопоточный с мультиплексированием ввода-вывода, PostgreSQL — процесс на соединение, Go — горутины (кооперативные потоки Go) поверх пула потоков.
Безопасность vs скорость: переход из пользовательского режима в ядро стоит 100–300 нс без защит от Spectre/Meltdown (аппаратные уязвимости спекулятивного исполнения) и 200–700 нс с KPTI (Kernel Page Table Isolation) и другими митигациями; конкретное значение зависит от поколения CPU и включённых защит. Механизмы, позволяющие обойтись без перехода в ядро (vDSO — виртуальный разделяемый объект), сокращают латентность на порядок. Общая память между ядром и приложением убирает системный вызов на критическом пути выполнения. Каждый шаг — компромисс между защитой и латентностью.
Надёжность (durability) vs латентность: запись в страничный кеш мгновенная, fsync() гарантирует запись на диск но стоит ~50 мкс (SSD) — ~10 мс (HDD). Базы данных балансируют между потерей данных при сбое и пропускной способностью (throughput).
Справедливость vs отзывчивость: планировщик балансирует через виртуальное время (механизм планировщика) — интерактивные потоки с низким виртуальным временем мгновенно получают CPU. Потоки реального времени нарушают справедливость ради гарантий латентности.
Атомарность vs пропускная способность: мьютексы (механизмы взаимного исключения) сериализуют доступ, структуры без блокировок дают параллелизм за счёт сложности (проблема повторного использования адресов, безопасное освобождение памяти). На практике мьютекс + правильная структура данных побеждает lock-free в большинстве случаев.
Абстракция vs контроль: мониторинг готовности дескрипторов абстрагирует ожидание, пакетная отправка запросов ядру (io_uring) даёт пропускную способность ценой сложности API. Обход страничного кеша (direct I/O) даёт контроль ценой потери упреждающего чтения.
См. также
- Аппаратное обеспечение — CPU, кеш, RAM, хранилище, шины
- PostgreSQL — shared_buffers и page cache, WAL и sequential I/O, MVCC и процессы
- Redis — event loop на epoll, fork+CoW для BGSAVE
- Ruby VM внутреннее устройство — GVL как
pthread_mutex_t+ condition variable, GC с bitmap marking (подготовка кfork() + CoW), Fiber как user-space кооперативные потоки, Ractor как почти-процессы с разделяемой памятью - System Design: кеширование — иерархия латентностей
Sources
- Michael Kerrisk, 2010, The Linux Programming Interface: https://man7.org/tlpi/
- Robert Love, 2010, Linux Kernel Development — 3rd edition: https://www.oreilly.com/library/view/linux-kernel-development/9780768696974/
- Brendan Gregg, 2019, BPF Performance Tools — Addison-Wesley: https://www.brendangregg.com/bpf-performance-tools-book.html
- Abraham Silberschatz, Peter B. Galvin, Greg Gagne, 2018, Operating System Concepts — 10th edition: https://www.os-book.com/OS10/