Linux: внутреннее устройство, графическая подсистема и разработка дистрибутива

Курс о том, как устроен Linux изнутри: ядро, процессы, память, файловые системы и загрузка. Отдельный блок посвящён графическому стеку (DRM/KMS, Mesa, X.Org/Wayland) и практикам создания собственного дистрибутива — от сборки пакетов до образов и репозиториев.

1. Архитектура Linux: ядро, user space, системные вызовы

Архитектура Linux: ядро, user space, системные вызовы

Linux часто описывают как ядро (kernel) плюс набор пользовательских программ (user space). Это упрощение полезно: оно объясняет, где проходит граница ответственности, почему одни ошибки «роняют» всю систему, а другие — только конкретное приложение, и как приложения вообще получают доступ к файлам, сети и устройствам.

Эта статья задаёт базовую модель, на которую мы будем опираться дальше при разборе графической подсистемы (DRM/KMS, Wayland/Xorg), а также при разработке дистрибутива (init-система, пакеты, зависимости, политики безопасности).

Общая картина: kernel space и user space

Ядро Linux работает в привилегированном режиме процессора и управляет ресурсами компьютера: CPU, памятью, устройствами, файлами, сетью. User space — это все обычные программы: оболочки, демоны, браузеры, графические серверы, утилиты.

Ключевая идея: приложение в user space не может напрямую управлять железом или «залезать» в память другого процесса. Вместо этого оно обращается к ядру через системные вызовы.

!Схема слоёв Linux и границы системных вызовов

Что такое ядро Linux

Ядро Linux — монолитное ядро с загружаемыми модулями. Это значит:

  • Основная логика (планировщик, управление памятью, VFS, сетевой стек и т.д.) работает в одном адресном пространстве kernel space.
  • Драйверы и некоторые подсистемы можно загружать/выгружать как модули без пересборки ядра.
  • Главные подсистемы ядра (в контексте курса)

  • Планировщик (scheduler): распределяет процессорное время между процессами/потоками.
  • Управление памятью (MM): виртуальная память, страничная адресация, mmap, page cache.
  • VFS (Virtual File System): единый интерфейс ко всем файловым системам и объектам вида «файл».
  • Драйверная модель: драйверы устройств, шины (PCI, USB), устройства /dev.
  • Сетевой стек: сокеты, маршрутизация, netfilter.
  • IPC: взаимодействие процессов (сигналы, пайпы, сокеты, shared memory и др.).
  • Официальное введение и индекс документации ядра: Linux kernel documentation.

    Что такое user space

    User space — это:

  • Приложения (например, bash, gcc, браузеры, графические окружения).
  • Демоны (например, systemd, sshd, NetworkManager).
  • Библиотеки (например, glibc, libstdc++, графические библиотеки).
  • Утилиты и инфраструктура дистрибутива (пакетный менеджер, скрипты, конфиги).
  • Важный мост между приложением и ядром — стандартная C-библиотека (обычно glibc), которая предоставляет удобные функции (open, read, fork) и внутри вызывает системные вызовы.

    Справочник по системным вызовам в man pages: syscalls(2).

    Процессы, потоки и изоляция

    Ядро изолирует процессы с помощью виртуальной памяти: каждый процесс видит своё адресное пространство. Потоки (threads) внутри одного процесса разделяют память, но планируются ядром как отдельные исполняемые сущности.

    Минимальные определения:

  • Процесс: экземпляр программы со своим адресным пространством и ресурсами.
  • Поток: поток исполнения внутри процесса; потоки одного процесса обычно разделяют память.
  • Контекст переключения: момент, когда ядро сохраняет состояние одного потока/процесса и восстанавливает состояние другого.
  • Системные вызовы: контракт между user space и ядром

    Системный вызов (syscall) — это контролируемый переход из user space в kernel space для выполнения привилегированной операции.

    Зачем нужны системные вызовы

  • Безопасность: приложение не получает прямой доступ к устройствам и памяти ядра.
  • Унификация: один и тот же интерфейс работает на разном железе.
  • Контроль ресурсов: ядро может ограничивать доступ, учитывать права, квоты, cgroups.
  • Что считается системным вызовом на практике

    Типичные группы:

  • Файлы и файловые дескрипторы: open, close, read, write, stat.
  • Процессы: fork, execve, wait.
  • Память: mmap, munmap, brk.
  • Сеть: socket, connect, sendto, recvfrom.
  • Синхронизация: futex (часто используется потоковыми библиотеками).
  • Подробная справка по open: open(2).

    Как вызов из C превращается в syscall

    Обычно приложение вызывает функцию библиотеки (например, open()), а библиотека:

  • Подготавливает аргументы в соответствии с ABI платформы.
  • Выполняет специальную инструкцию перехода в ядро (например, syscall на x86_64).
  • Получает результат и переводит ошибки ядра в привычный вид (например, возвращает -1 и устанавливает errno).
  • Минимальный пример: открыть файл и прочитать данные

    Что здесь важно для архитектуры:

  • open/read/write/close выглядят как обычные функции, но внутри приводят к системным вызовам.
  • Ядро возвращает файловый дескриптор fd — это число, через которое процесс обращается к открытому объекту.
  • Возврат ошибок и errno

    Ядро сообщает об ошибках через коды возврата. Библиотека (например, glibc) обычно преобразует это так, чтобы C-код мог писать:

  • Проверить возвращаемое значение (-1 для многих функций).
  • Прочитать причину в errno.
  • Документация по errno: errno(3).

    VFS и идея «всё — файл»

    Фраза «в Linux всё — файл» не означает, что буквально всё хранится на диске. Она означает, что многие ресурсы представлены единым файловым интерфейсом: можно использовать похожие операции (open/read/write/ioctl) для работы с разными сущностями.

    Примеры:

  • Обычные файлы на ext4.
  • Устройства в /dev (например, /dev/null).
  • Псевдофайловые системы:
  • - /proc — информация о процессах и ядре. - /sys — интерфейс к устройствам и драйверам (sysfs).

    Справка по /proc: proc(5).

    Специальный случай: ioctl

    Иногда чтения/записи недостаточно, и нужен расширенный интерфейс. Тогда используют ioctl — универсальную точку входа для «команд» устройству или драйверу.

    Это особенно важно для графики и устройств: многие интерфейсы DRM, терминалы, сетевые настройки исторически опираются на ioctl.

    Справка: ioctl(2).

    Наблюдаем системные вызовы: strace

    Чтобы связать теорию с практикой, полезно увидеть syscalls «вживую». Утилита strace перехватывает системные вызовы процесса и печатает их.

    Примеры:

  • -f включает отслеживание дочерних процессов.
  • -o пишет вывод в файл.
  • Полезная фильтрация:

    В современных системах часто используется openat вместо open — это нормальная эволюция интерфейса syscalls.

    Почему это важно для разработки дистрибутива и графической подсистемы

    Связь с дальнейшими темами курса:

  • Графическая подсистема (DRM/KMS, Wayland, Xorg) — это в значительной степени взаимодействие user space-компонентов с драйверами ядра через файловые дескрипторы и ioctl.
  • Init-система и сервисы дистрибутива запускают процессы, управляют правами, cgroups, namespace-изоляцией — всё это опирается на системные вызовы.
  • Отладка проблем в дистрибутиве часто сводится к вопросу: какой именно syscall не сработал, какое право доступа/контекст безопасности помешал, какой файл/устройство не доступно.
  • Краткое резюме

  • Ядро (kernel space) управляет ресурсами и предоставляет интерфейс системных вызовов.
  • User space — это приложения, демоны и библиотеки, которые не имеют прямого доступа к привилегированным операциям.
  • Системные вызовы — контракт между ними; чаще всего вы взаимодействуете с ними через функции стандартных библиотек.
  • VFS и файловые дескрипторы дают единый способ работы с файлами, устройствами и многими интерфейсами ядра.
  • strace помогает увидеть реальную картину взаимодействия приложения с ядром.
  • 2. Процессы, планировщик, память и IPC

    Процессы, планировщик, память и IPC

    В предыдущей статье мы зафиксировали границу между user space и kernel space и разобрали, как приложения попадают в ядро через системные вызовы. Теперь углубимся в то, чем ядро занимается «внутри»: как оно исполняет процессы и потоки, делит процессорное время, управляет виртуальной памятью и организует взаимодействие между процессами (IPC). Эти темы понадобятся дальше при разборе графической подсистемы (Wayland/Xorg, DRM/KMS), где важны задержки планировщика, копирование данных и выбор IPC-механизма, и при разработке дистрибутива, где вы будете настраивать лимиты ресурсов, политику памяти и сервисы.

    Процессы и потоки: что именно планирует ядро

    Процесс в Linux — это выполняемая программа со своим виртуальным адресным пространством и набором ресурсов (дескрипторы файлов, обработчики сигналов, права доступа и т.д.).

    Поток (thread) — отдельный поток исполнения внутри процесса. Потоки одного процесса обычно разделяют адресное пространство и большинство ресурсов, но планируются ядром независимо.

    В Linux важная практическая деталь: ядро оперирует сущностью task (часто в документации и инструментах вы увидите «процесс/поток» почти как синонимы). Создание потока в user space (например, через pthread) в итоге приводит к созданию новой task в ядре.

    Идентификаторы и что вы видите в инструментах

  • PID — идентификатор процесса.
  • TID — идентификатор потока (в Linux поток тоже имеет ID; часто TID == PID у главного потока).
  • PPID — PID родительского процесса.
  • Полезные справочники:

  • Описание procfs
  • Системный вызов clone
  • Жизненный цикл процесса: fork/exec/exit

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

  • Родитель создаёт копию себя через fork().
  • В дочернем процессе заменяет образ процесса на новую программу через execve().
  • Завершение — exit(), а родитель получает статус через wait().
  • Ссылки:

  • fork(2)
  • execve(2)
  • wait(2)
  • Состояния задач и переключение контекста

    Упрощённо задача может быть:

  • Running — выполняется на CPU прямо сейчас.
  • Runnable — готова выполняться, ждёт CPU.
  • Sleeping — ждёт событие (I/O, таймер, futex и т.д.).
  • Stopped/Traced — остановлена сигналом или отладчиком.
  • Zombie — уже завершилась, но родитель ещё не прочитал статус (остаётся запись в таблице процессов).
  • !Схема переходов между состояниями процесса/потока

    Переключение контекста — момент, когда ядро сохраняет состояние одной задачи (регистры CPU, указатели на таблицы страниц и т.д.) и восстанавливает состояние другой. Это неизбежно в многозадачности, но само по себе стоит времени, поэтому архитектура приложений (особенно графических и мультимедийных) часто старается избегать лишних пробуждений и конкуренции за блокировки.

    Планировщик Linux: как делится CPU

    Планировщик решает, какая задача получит CPU и на сколько.

    CFS и справедливость

    Для обычных задач Linux использует планировщик CFS (Completely Fair Scheduler). Интуиция простая:

  • все runnable-задачи должны получать «справедливую долю» CPU;
  • если задача долго не получала CPU, её «должны догнать».
  • Важная практическая настройка — nice (от -20 до 19): это не «процент CPU», а подсказка планировщику, как соотносить веса задач.

    Справочник:

  • sched(7)
  • nice(1)
  • Реальное время: когда важнее задержка, а не пропускная способность

    Существуют real-time политики (SCHED_FIFO, SCHED_RR), которые нужны там, где критичны задержки. Они опаснее в администрировании: неправильная настройка может «вытеснить» обычные задачи и ухудшить отзывчивость системы.

    Справочник:

  • sched_setscheduler(2)
  • Что влияет на отзывчивость системы

  • Количество runnable-задач (конкуренция за CPU).
  • Частые пробуждения (особенно таймерные) и переключения контекста.
  • I/O ожидания: часть задач спит, но может создавать «шторм» пробуждений.
  • Приоритеты, nice, real-time политики.
  • Практика наблюдения:

  • -L показывает потоки.
  • cls/pri/ni помогают увидеть класс планирования, приоритет и nice.
  • Виртуальная память: почему процесс «видит» больше, чем есть RAM

    Каждый процесс работает в своём виртуальном адресном пространстве. Это даёт:

  • изоляцию (процессы не могут читать/писать память друг друга напрямую);
  • удобную модель для программиста (память выглядит непрерывной);
  • возможность лениво выделять память и подгружать страницы по требованию.
  • Ключевые термины:

  • Страница памяти — минимальная единица управления (часто 4 KiB, но зависит от архитектуры и настроек).
  • Таблицы страниц — структуры, которые отображают виртуальные страницы на физические.
  • Page fault — событие, когда обращение к виртуальному адресу требует вмешательства ядра (например, страница ещё не загружена, или доступ запрещён).
  • Ссылки:

  • mmap(2)
  • О разделах /proc/PID/maps и smaps
  • Карта памяти процесса и mmap

    Процесс обычно имеет области:

  • код программы и разделяемые библиотеки;
  • heap (динамическая память, malloc);
  • stack (стек);
  • memory-mapped области (mmap): файлы, shared memory, анонимные отображения.
  • Посмотреть карту:

    Copy-on-write: почему fork «дешёвый» до первой записи

    После fork() память логически копируется, но физически страницы часто разделяются до тех пор, пока один из процессов не попытается записать в страницу. Тогда возникает page fault, и ядро создаёт копию страницы — это и есть copy-on-write.

    Это критично для производительности: массовые fork() не обязаны немедленно удваивать потребление RAM.

    Page cache: файловое I/O часто работает через память

    При чтении файлов данные обычно попадают в page cache — кэш страниц файлов в RAM. Это ускоряет повторное чтение и позволяет делать отложенную запись.

    Следствие для практики:

  • «свободная память» в Linux часто маленькая, потому что RAM используется под кэш;
  • это не обязательно проблема: кэш освобождается при необходимости.
  • Справочник:

  • Документация по procfs (MemAvailable и детали)
  • Overcommit, swap и OOM

    Linux может разрешать выделение виртуальной памяти «с запасом» (overcommit): процессу обещают адресное пространство, но физически RAM/Swap может появиться позже. Если в момент реального использования памяти ресурсов не хватает, возможен OOM (out-of-memory): ядро вынужденно завершит один или несколько процессов.

    В контексте дистрибутива это важно:

  • вы будете выбирать политики (swap, overcommit) и лимиты (например, через cgroups), чтобы система деградировала предсказуемо, а не падала случайными приложениями.
  • Ссылка:

  • Описания sysctl vm.overcommit_*
  • IPC: как процессы общаются между собой

    IPC (inter-process communication) — механизмы обмена данными и сигналами между процессами. В Linux почти все IPC опираются на уже знакомые идеи: файловые дескрипторы, VFS и системные вызовы.

    Сигналы: «сообщения» управления

    Сигналы — асинхронные уведомления (например, SIGTERM, SIGINT, SIGCHLD). Они удобны для управления жизненным циклом и простых событий, но не подходят для передачи больших данных.

    Справочник:

  • signal(7)
  • Каналы: pipe и FIFO

  • Pipe — однонаправленный канал между процессами (часто родитель-дочь), создаётся pipe().
  • FIFO (именованный канал) живёт как объект файловой системы.
  • Ссылки:

  • pipe(2)
  • fifo(7)
  • Unix domain sockets: универсальный IPC на одной машине

    Unix-сокеты часто используются для клиент-серверного взаимодействия на одном хосте. Они поддерживают:

  • потоковый или датаграммный режим;
  • передачу файловых дескрипторов между процессами (важно для композиции сервисов).
  • Ссылки:

  • unix(7)
  • sendmsg(2) и SCM_RIGHTS
  • Shared memory: когда нужно быстро передавать большие буферы

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

    Варианты:

  • POSIX shared memory (shm_open + mmap).
  • Анонимная shared memory через memfd_create (часто встречается в современных графических стэках).
  • Ссылки:

  • shm_overview(7)
  • memfd_create(2)
  • Futex: основа быстрых блокировок

    Многие примитивы синхронизации в user space (mutex/condvar) в нормальном случае работают без входа в ядро, а в случае конкуренции используют futex — механизм «спи/проснись» на адресе в памяти.

    Ссылка:

  • futex(2)
  • !Сравнение IPC: сокеты для управления и shared memory для больших данных

    Связь с графической подсистемой и разработкой дистрибутива

  • В графике задержки планировщика и конкуренция потоков напрямую влияют на джиттер и «фризы»: compositor и рендер-потоки чувствительны к пробуждениям и приоритетам.
  • Wayland-композиторы и клиенты обычно общаются через Unix-сокеты, а буферы кадра передают через разделяемую память или dma-buf (дальше в курсе). Выбор IPC определяет количество копирований.
  • В дистрибутиве вы будете настраивать сервисы и лимиты: сколько процессов можно создать, сколько памяти можно потреблять, как система реагирует на OOM. Для этого важно понимать, что именно лимитируется (процессы/потоки как tasks, виртуальная память, RSS, page cache).
  • Краткое резюме

  • Процесс — адресное пространство и ресурсы, поток — отдельная планируемая единица исполнения; в Linux они представлены как tasks.
  • Планировщик (CFS для обычных задач) делит CPU с акцентом на справедливость; nice и классы планирования влияют на распределение и задержки.
  • Виртуальная память изолирует процессы, использует страницы и page fault; mmap и copy-on-write важны для производительности.
  • Page cache делает файловое I/O «похожим на работу с памятью» и объясняет, почему RAM активно используется.
  • IPC включает сигналы, pipe/FIFO, Unix-сокеты, shared memory и futex; выбор механизма определяет задержки и объём копирования данных.
  • 3. Файловые системы, VFS, устройства и udev

    Файловые системы, VFS, устройства и udev

    В прошлых статьях мы закрепили границу user space и kernel space, а также разобрали процессы, память и IPC. Теперь соберём ещё один фундаментальный слой: как Linux представляет данные и устройства через единый файловый интерфейс, как работает VFS (Virtual File System), чем отличаются блочные и символьные устройства, и как udev создаёт в /dev “правильные” имена и права доступа.

    Эта тема напрямую пригодится дальше:

  • В графической подсистеме доступ к GPU идёт через файлы устройств вроде /dev/dri/card0 и /dev/dri/renderD128.
  • При разработке дистрибутива вам нужно понимать, как появляется корневая ФС, кто создаёт /dev, почему “иногда нет диска” и где искать истину — в /proc, /sys и логах udev.
  • От системного вызова к файловой системе: где находится VFS

    Когда приложение делает open, read, write, stat или ioctl, оно попадает в ядро через системные вызовы (это мы уже видели и через strace). Дальше ядро должно понять, что такое путь /home/user/file и какой драйвер должен обслужить обращение.

    Этим занимается VFS — слой ядра, который:

  • Даёт единый интерфейс для разных файловых систем (ext4, XFS, Btrfs, tmpfs и других).
  • Реализует общую логику: разбор путей, права доступа, работу с дескрипторами.
  • Перенаправляет операции конкретной файловой системе или псевдофайловой системе.
  • Документация: Virtual File System (VFS)

    !Как VFS соединяет вызовы приложений с конкретной файловой системой и устройствами

    “Всё — файл” в Linux: что это означает на практике

    Фраза “всё — файл” означает, что ядро старается представить разные объекты через похожие операции: открыть, прочитать, записать, выполнить управляющую команду.

    Через файловый интерфейс работают:

  • Обычные файлы и каталоги.
  • Псевдофайлы (например, /proc и /sys).
  • Файлы устройств в /dev.
  • Сокеты и FIFO.
  • Чтобы понять, какой тип объекта перед вами, используют stat.

    Справочник: stat(2)

    Типы файлов (что вы видите в ls -l)

    | Тип объекта | Первый символ в ls -l | Пример | Зачем нужен | |---|---|---|---| | Обычный файл | - | /etc/os-release | Данные на диске или в tmpfs | | Каталог | d | /home | Имена -> ссылки на объекты | | Символьная ссылка | l | /bin -> /usr/bin | Перенаправление пути | | Символьное устройство | c | /dev/null | Потоковое устройство, ioctl | | Блочное устройство | b | /dev/sda | Доступ блоками, ФС поверх | | Сокет | s | /run/systemd/private | IPC (часто Unix-socket) | | FIFO (pipe) | p | именованный канал | IPC “как файл” |

    Базовые структуры VFS: inode, dentry, superblock

    Чтобы обслужить обращение к файлу, VFS опирается на несколько ключевых сущностей.

    inode: “что это за объект”

    inode — структура, описывающая объект файловой системы: тип, права, владельца, размер, временные метки, и (в случае обычных ФС) указатели на данные.

    Важно: имя файла не хранится в inode напрямую. Имя живёт в каталоге.

    Справочник: inode(7)

    dentry: “как имя связано с inode”

    dentry (directory entry) — элемент, связывающий имя в каталоге с inode. Ядро активно кеширует dentry, чтобы ускорять разбор путей.

    Практическое следствие: даже операции, которые “просто проверяют путь”, могут быть быстрыми за счёт dentry cache.

    superblock: “что это за смонтированная ФС”

    superblock описывает смонтированную файловую систему: её тип, параметры, состояние, набор операций.

    Когда вы делаете mount, ядро создаёт/использует superblock и присоединяет дерево ФС в общую иерархию.

    Справочник: mount(8)

    Монтирование и единое дерево

    Linux собирает множество файловых систем в одно дерево, где корень — /.

    Типичные точки монтирования:

  • / — корневая файловая система.
  • /home — отдельный раздел для данных.
  • /boot — раздел для загрузчика и ядра.
  • /proc, /sys, /run — псевдофайловые системы.
  • Команды для наблюдения:

    Справочник: proc(5)

    Псевдофайловые системы: /proc, /sys, tmpfs, devtmpfs

    Не все “файлы” лежат на диске.

    procfs: /proc

    /proc показывает состояние ядра и процессов. Многие инструменты (ps, top) читают данные именно отсюда.

    Справочник: proc(5)

    sysfs: /sys

    /sys (sysfs) — отражение модели устройств ядра: устройства, драйверы, шины, атрибуты. Это ключевое место, где udev берёт информацию для именования устройств.

    Справочник: sysfs(5)

    tmpfs: “файловая система в памяти”

    tmpfs живёт в RAM (и может выгружать страницы в swap). Часто на tmpfs расположен /run и иногда /tmp.

    Справочник: tmpfs(5)

    devtmpfs: базовый /dev от ядра

    devtmpfs — специальная ФС, где ядро само создаёт базовые файлы устройств, когда драйвер регистрирует устройство.

    На практике udev обычно “достраивает” поверх этого: меняет права, создаёт симлинки, делает предсказуемые имена.

    Справочник: devtmpfs(5)

    Устройства как файлы: блочные и символьные

    Файл устройства — это не драйвер и не данные, а точка входа в драйвер через VFS.

    Символьные устройства (character devices)

    Символьные устройства работают как поток байт (не обязательно “последовательно”, но интерфейс потоковый). Типично активно используются ioctl.

    Примеры:

  • /dev/null, /dev/zero
  • /dev/tty, /dev/input/event*
  • /dev/dri/card0, /dev/dri/renderD128 (GPU)
  • Блочные устройства (block devices)

    Блочные устройства оптимизированы под хранение данных блоками и обычно используются как основа для файловых систем.

    Примеры:

  • /dev/sda, /dev/nvme0n1, /dev/mmcblk0
  • Наблюдение:

    Major/Minor: как ядро понимает, какой драйвер обслуживает /dev

    У каждого файла устройства есть пара чисел:

  • major — “какой драйвер/класс устройств обслуживает запросы”.
  • minor — “какое конкретно устройство/экземпляр внутри major”.
  • Посмотреть можно так:

    Создать вручную можно через mknod, но в современных системах это почти всегда делает devtmpfs+udev.

    Справочник: mknod(1)

    Как появляется /dev: роль udev

    События ядра: uevent

    Когда в системе появляется устройство (или меняется его состояние), ядро генерирует событие uevent и публикует его в user space через netlink. User space-демон udev слушает эти события и применяет правила.

    systemd-udevd: менеджер устройств в user space

    В большинстве дистрибутивов udev реализован как systemd-udevd. Его задачи:

  • Создавать/удалять узлы устройств и симлинки.
  • Выставлять владельца/группу/права доступа.
  • Присваивать “постоянные” имена (например, по UUID/серийному номеру).
  • Запускать дополнительные действия по правилам (в пределах разумного, так как это влияет на время обнаружения устройств).
  • Документация: udev — Device Manager for the Linux kernel

    !Как события ядра превращаются в имена и права в /dev

    Практика: смотреть события и атрибуты udev

    Мониторинг событий:

    Посмотреть, какие атрибуты udev знает про устройство:

    Посмотреть дерево устройств и связи с драйверами:

    Справочник: udevadm(8)

    Предсказуемые имена: почему “/dev/sda” недостаточно

    Имена типа /dev/sda зависят от порядка обнаружения устройств и могут меняться. Поэтому дистрибутивы и администраторы часто используют устойчивые идентификаторы:

  • /dev/disk/by-uuid/…
  • /dev/disk/by-label/…
  • /dev/disk/by-id/…
  • /dev/disk/by-path/…
  • Это создаётся udev на основе данных из sysfs и метаданных блочных устройств.

    Практика:

    Права доступа к устройствам: безопасность и удобство

    Так как устройства представлены файлами, к ним применяются обычные права (владелец/группа/режим). Это критично для безопасности.

    Типичные примеры:

  • GPU-устройства часто доступны группе video или render.
  • Устройства ввода (/dev/input/event*) обычно защищены, чтобы приложения не могли легко перехватывать ввод.
  • udev-правила задают эти права централизованно.

    Связь с графической подсистемой

    Современный графический стек Linux опирается на файлы устройств DRM:

  • /dev/dri/cardN — “primary node”, связан с режимами отображения (KMS) и часто требует более привилегированного доступа.
  • /dev/dri/renderDN — “render node”, предназначен для рендеринга без прямого управления дисплеем; обычно доступнее для приложений.
  • Ошибки уровня “не стартует compositor” или “не работает аппаратное ускорение” часто сводятся к одному из пунктов:

  • Нет нужного узла в /dev/dri (драйвер не загрузился или нет прав).
  • Права/группы выставлены не так (udev rules, политики безопасности).
  • В контейнере/namespace не проброшены нужные устройства.
  • Связь с разработкой дистрибутива

    Если вы собираете минимальную систему или свой дистрибутив, вопросы “как оно загружается” упираются в файловые системы и /dev:

  • Нужно смонтировать корень / и базовые псевдо-ФС (/proc, /sys, часто /run).
  • Нужно обеспечить наличие /dev (обычно devtmpfs) и запуск udev для корректных имён и прав.
  • Нужно выбрать правила именования и политики доступа (например, что доступно обычному пользователю).
  • Нужно решить, как находить корневой раздел: по UUID/Label (устойчиво) или по /dev/sdX (хрупко).
  • Минимальный чек-лист диагностики “пропало устройство”

  • Проверить, видит ли ядро устройство в sysfs: ls /sys/class и соответствующие каталоги.
  • Посмотреть uevent и работу udev: udevadm monitor --kernel --udev.
  • Проверить наличие узла в /dev и его тип/права: ls -l /dev/....
  • Для блочных устройств: lsblk -f, blkid, симлинки /dev/disk/by-*.
  • Убедиться, что нужная ФС смонтирована и в нужное место: findmnt.
  • Краткое резюме

  • VFS — слой ядра, который даёт единый интерфейс файлов и устройств и маршрутизирует операции к конкретным файловым системам и драйверам.
  • inode описывает объект, dentry связывает имя с объектом, superblock описывает смонтированную ФС.
  • /proc, /sys, tmpfs, devtmpfs — важные псевдофайловые системы; sysfs и devtmpfs критичны для работы с устройствами.
  • Устройства представлены файлами в /dev (блочные и символьные) и идентифицируются major/minor.
  • udev (обычно systemd-udevd) реагирует на события ядра, создаёт узлы в /dev, устойчивые симлинки и выставляет права; без него система часто остаётся “формально рабочей”, но неудобной и небезопасной.
  • 4. Загрузка системы: BIOS/UEFI, bootloader, initramfs, systemd

    Загрузка системы: BIOS/UEFI, bootloader, initramfs, systemd

    Загрузка Linux — это цепочка взаимосвязанных этапов, где каждый следующий шаг опирается на предыдущий. Для разработчика дистрибутива это особенно важно: ошибки в загрузке часто выглядят как «чёрный экран» или «не найден диск», но почти всегда имеют конкретную причину на конкретном этапе.

    Связь с предыдущими статьями курса:

  • Мы уже разобрали, что ядро и user space разделены границей системных вызовов.
  • Мы видели, что VFS и /dev — основа доступа к дискам и устройствам.
  • Теперь соберём воедино, как система вообще доходит до момента, когда есть смонтированный корень, работающий /dev, и запущен PID 1 (обычно systemd).
  • !Общая картина: кто и в каком порядке поднимает систему

    Прошивка: BIOS и UEFI

    До запуска ядра процесс контролирует прошивка материнской платы.

    BIOS (исторический режим)

    BIOS выполняет начальную инициализацию железа и ищет загрузочный код на выбранном устройстве. Классический вариант — загрузка первых секторов диска (MBR), после чего управление переходит к загрузчику.

    С точки зрения современного Linux BIOS-режим важен главным образом для совместимости со старым железом и старыми схемами разметки.

    UEFI (современный режим)

    UEFI заменяет BIOS и предоставляет более структурированную модель загрузки:

  • Есть EFI System Partition (ESP) — специальный раздел (обычно FAT32), где лежат EFI-приложения загрузчиков.
  • Прошивка запускает выбранный EFI-загрузчик как программу.
  • Поддерживаются записи в NVRAM (BootOrder/BootXXXX) и загрузка по путям на ESP.
  • С практической точки зрения для дистрибутива это означает:

  • Нужно уметь установить загрузчик на ESP.
  • Нужно понимать, как формируется путь вида \EFI\<vendor>\<loader>.efi.
  • Документация и спецификации:

  • UEFI Specifications
  • Bootloader: что он делает на самом деле

    Загрузчик (bootloader) — компонент между прошивкой и ядром. Он решает задачу: выбрать ОС/ядро, загрузить ядро в память, передать параметры и (часто) передать initramfs.

    Наиболее распространённые варианты:

  • GRUB — гибкий, поддерживает сложные конфигурации, меню, цепочки загрузки.
  • systemd-boot — проще, рассчитан на UEFI и типовые сценарии.
  • Ключевые функции bootloader:

  • Загрузка файла ядра (обычно vmlinuz или bzImage).
  • Загрузка initramfs (если используется).
  • Передача командной строки ядра (kernel cmdline).
  • Полезные ссылки:

  • GNU GRUB Manual
  • systemd-boot
  • Командная строка ядра (kernel cmdline)

    Bootloader передаёт ядру строку параметров. Она влияет на раннюю инициализацию: где искать корень, как вести себя с логами, какие драйверы/режимы включать.

    Типичные параметры:

  • root= — где находится корневая файловая система.
  • ro/rw — монтировать корень read-only или read-write на первом этапе.
  • quiet, loglevel= — насколько подробны сообщения ядра.
  • Справочник:

  • Kernel parameters
  • Старт ядра: от распаковки до первого userspace

    После того как bootloader передал управление ядру, начинается чисто kernel-этап.

    Что важно понимать концептуально:

  • Ядро поднимает минимальные подсистемы, без которых нельзя продолжить: память, планировщик, базовые драйверы, VFS-структуры.
  • Ядро должно в какой-то момент запустить первый процесс в user space — PID 1. Исторически это мог быть /sbin/init, а сегодня чаще всего это systemd.
  • Ранние сообщения ядра можно смотреть так:

    В systemd-системах сообщения ядра также попадают в journal:

    Зачем нужен initramfs

    initramfs — это архив (обычно cpio), который bootloader загружает вместе с ядром. Ядро распаковывает его в RAM и временно использует как корневую файловую систему, чтобы выполнить ранние шаги подготовки.

    Важно не путать:

  • initramfs — механизм и образ, который распаковывается в память.
  • initrd — исторический термин; в современных системах обычно говорят initramfs, хотя слово initrd всё ещё встречается в документации и параметрах.
  • Документация ядра:

  • Using initramfs
  • Какие проблемы решает initramfs

    Без initramfs ядро должно уметь смонтировать корневую ФС сразу. Это не всегда возможно, потому что корень может находиться на сложном стеке:

  • LVM
  • RAID
  • LUKS/dm-crypt (шифрование)
  • сетевой корень
  • файловая система, для которой драйвер/модуль ещё надо загрузить
  • Initramfs позволяет сделать ранний userspace, который:

  • Загружает нужные модули ядра.
  • Поднимает нужные виртуальные ФС (/proc, /sys, часто devtmpfs как /dev).
  • Находит реальный корень по UUID/label.
  • Расшифровывает диск, собирает LVM/RAID.
  • Монтирует реальный root и выполняет переход на него.
  • Что внутри initramfs

    Минимально внутри есть:

  • init или скрипт /init — первая программа, которую запускает ядро в этой временной среде.
  • Утилиты, необходимые для сборки корня (в зависимости от генератора initramfs).
  • Конфигурация и hooks.
  • Идея напрямую связана с темой VFS из прошлой статьи: до появления «настоящего» корня система живёт на временном корне в RAM, но всё равно использует VFS и монтирование.

    Посмотреть, какие файлы в initramfs (команды зависят от дистрибутива):

    Ранний userspace: /proc, /sys, /dev и поиск корня

    Когда ядро распаковало initramfs, оно запускает /init (или другой указанный init), и начинается ранний userspace.

    Здесь проявляется связь с темой «устройства и udev»:

  • Чтобы обнаружить диски, нужны узлы устройств и/или информация из sysfs.
  • Обычно монтируются:
  • /proc (procfs)
  • /sys (sysfs)
  • /dev (часто devtmpfs, а затем подключается udev-логика)
  • Дальше выполняется поиск реального root:

  • по параметру root= (например, UUID)
  • по конфигурации initramfs
  • через сценарии, которые понимают LVM/LUKS/RAID
  • Переход на реальный root: switch_root

    Когда реальная корневая файловая система найдена и смонтирована, временный корень initramfs должен уступить место настоящему.

    Это обычно делается через:

  • switch_root (часто в initramfs)
  • или эквивалентную последовательность pivot_root + перенос точек монтирования (реализации различаются)
  • Ключевая идея:

  • После перехода PID 1 (или процесс, который запустит PID 1) должен оказаться уже в контексте реального /.
  • При этом важно корректно «перенести» критические монтирования (/proc, /sys, /dev), иначе дальше начнутся странные сбои.
  • systemd как PID 1: что он делает в загрузке

    После перехода на настоящий root система запускает PID 1. В большинстве современных дистрибутивов это systemd.

    Документация:

  • systemd
  • bootup
  • systemd.special
  • Почему PID 1 особенный

    PID 1 в Linux имеет уникальную роль:

  • Он становится родителем для процессов, которые «осиротели».
  • Он отвечает за запуск и остановку системных сервисов.
  • Он участвует в обработке завершения системы (shutdown/reboot).
  • Связь с темой «процессы и планировщик» из прошлой статьи:

  • systemd порождает множество процессов и следит за их состоянием.
  • Ошибки на старте часто выглядят как падение сервиса, но причина может быть в правах доступа к /dev, отсутствующих монтированиях или неверной зависимости.
  • Units, зависимости и targets

    Systemd управляет юнитами (units): сервисы, точки монтирования, устройства, таймеры и другие сущности.

    Типы, которые чаще всего важны для понимания загрузки:

  • service — демоны и одноразовые задачи.
  • mount — монтирование ФС.
  • device — появление устройств (интеграция с udev).
  • target — логические «точки сборки» зависимостей.
  • Типичные цели загрузки:

  • multi-user.target — текстовый многопользовательский режим.
  • graphical.target — графический режим (важно для будущего блока про графическую подсистему).
  • Проверить, куда настроена загрузка по умолчанию:

    udev в мире systemd

    Во многих дистрибутивах udev — это systemd-udevd. Он критичен для корректного /dev:

  • создаёт симлинки /dev/disk/by-*
  • выставляет права на устройства
  • позволяет сервисам «ждать» появления нужного устройства через зависимости systemd
  • Это прямое продолжение темы из статьи про устройства и udev: без корректной работы udev система может загрузиться, но у пользователя «не будет дисков», «не будет сети» или «не будет GPU-доступа».

    Как диагностировать проблемы загрузки

    Практический минимум, который полезен и разработчику дистрибутива, и инженеру:

  • Посмотреть логи текущей загрузки:
  • Посмотреть критические ошибки:
  • Понять, что тормозит загрузку:
  • Проверить, какие параметры реально получил kernel:
  • Понять, смонтировалось ли всё ожидаемое:
  • Что важно при разработке дистрибутива

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

  • Режим загрузки: BIOS, UEFI или оба.
  • Выбор загрузчика: GRUB (универсальнее) или systemd-boot (проще для UEFI).
  • Будет ли initramfs обязательным:
  • Без initramfs проще стек, но меньше совместимость.
  • С initramfs проще поддерживать LUKS/LVM/RAID и разные драйверы, но появляется ещё один артефакт сборки.
  • Как обеспечивать ранний /dev и правила прав:
  • Нужен devtmpfs.
  • Нужен udev (или осознанная альтернатива), иначе устройства и права будут «сырыми».
  • Какая init-система:
  • systemd — наиболее распространённый вариант, особенно если вы хотите современную интеграцию с udev, журналом и зависимостями.
  • Краткое резюме

  • BIOS/UEFI подготавливают запуск и передают управление загрузчику.
  • Bootloader выбирает и загружает ядро, initramfs и параметры kernel cmdline.
  • Ядро поднимает базовые подсистемы и запускает первый userspace через initramfs (если он есть).
  • Initramfs готовит систему к монтированию настоящего корня и выполняет переход на него.
  • После перехода запускается PID 1 (обычно systemd), который поднимает сервисы, монтирования и udev, доводя систему до multi-user.target или graphical.target.
  • 5. Графический стек: DRM/KMS, Mesa, драйверы и ввод

    Графический стек: DRM/KMS, Mesa, драйверы и ввод

    Графика в Linux — это не один «графический сервер», а цепочка компонентов в kernel space и user space. В прошлых статьях мы разобрали системные вызовы, файловые дескрипторы, VFS и /dev, а также загрузку и роль systemd и udev. Теперь соберём это в единую картину: как приложение получает аппаратное ускорение, как настраиваются режимы дисплея, почему существуют /dev/dri/card0 и /dev/dri/renderD128, и как события клавиатуры/мыши доходят до окна.

    Эта статья — мост к следующим темам графической подсистемы (Wayland/Xorg, compositor, буферы кадра, zero-copy), а также к практике разработки дистрибутива: какие пакеты и сервисы нужны, какие права должны быть выставлены, и где искать причины «чёрного экрана».

    !Общая карта компонентов и границ между user space и kernel space

    Ключевые роли в графике: что в ядре, а что в user space

    В терминах курса:

  • Ядро отвечает за:
  • - управление GPU как устройством; - безопасность доступа к устройству через файлы в /dev; - вывод на дисплей и переключение режимов (разрешение, частота) через KMS; - предоставление механизмов буферов и синхронизации.
  • User space отвечает за:
  • - выбор графического протокола и модели окон (Wayland/X11); - рендеринг (через Mesa или проприетарные стеки); - обработку ввода (libinput) и маршрутизацию событий в окна; - композицию кадров (compositor).

    Практически вся связь user space с графикой проходит через привычные механизмы из предыдущих статей:

  • файловые дескрипторы (open на /dev/dri/, /dev/input/event);
  • ioctl как основной интерфейс управления драйвером;
  • права доступа и группы, выставляемые udev.
  • DRM и KMS: базовая графическая подсистема ядра

    Что такое DRM

    DRM (Direct Rendering Manager) — подсистема ядра Linux для работы с GPU и дисплеями. Исторически она появилась как способ безопасно и эффективно отдавать приложениям возможность «рендерить напрямую», не делая всю графику привилегированной.

    DRM в ядре включает:

  • интерфейсы, доступные через устройства в /dev/dri/*;
  • драйверы конкретных GPU (например, i915 для Intel, amdgpu для AMD, nouveau для NVIDIA);
  • менеджмент буферов (создание, привязка, синхронизация);
  • интеграцию с подсистемой вывода (KMS).
  • Официальная документация по DRM в ядре: DRM documentation (Linux kernel)

    Что такое KMS

    KMS (Kernel Mode Setting) — часть DRM, отвечающая за режимы отображения и «проведение» изображения до монитора.

    KMS оперирует понятиями:

  • connector — физический выход (HDMI, DisplayPort, eDP для встроенного экрана);
  • encoder — логический узел, кодирующий поток для коннектора;
  • CRTC — блок, который формирует видеосигнал (исторический термин, но сущность актуальна);
  • plane — слой (primary/cursor/overlay), который можно смешивать аппаратно;
  • framebuffer — буфер с пикселями, который показывается на экране.
  • Это важно для Wayland-композитора: он не просто «рисует окна», он ещё и программирует KMS, выбирая какие буферы и в какие плоскости вывести.

    Документация по KMS как части DRM: KMS (Kernel Mode Setting) overview (Linux kernel)

    Узлы устройств DRM: /dev/dri/card и /dev/dri/renderD

    Из статьи про VFS и устройства вы уже знаете, что доступ к железу в Linux обычно идёт через файлы в /dev. Для DRM это критично: графика не «магия», а набор open и ioctl к устройству.

    Типичные узлы:

  • /dev/dri/cardNprimary node: используется для задач, связанных с KMS (управление выводом). Доступ чаще ограничен.
  • /dev/dri/renderDNrender node: предназначен для рендеринга без контроля над выводом на дисплей. Обычно именно его получают приложения для OpenGL/Vulkan.
  • Практическая диагностика:

    Если аппаратное ускорение «вдруг пропало», очень часто причина — права на renderD* (группа render) или то, что нужный модуль драйвера не загрузился.

    Mesa: где живут OpenGL и Vulkan в большинстве Linux-дистрибутивов

    Mesa — основной открытый стек 3D-графики в user space. Он реализует:

  • OpenGL (через библиотеки вроде libGL);
  • Vulkan (через ICD-драйверы, например RADV для AMD, ANV для Intel);
  • часть 2D/видео-инфраструктуры (в зависимости от сборки и драйверов).
  • Документация Mesa: Mesa documentation

    Как Mesa связана с ядром

    Mesa сама по себе не управляет железом напрямую. Она:

  • открывает DRM-устройство (обычно render node) в /dev/dri;
  • общается с драйвером ядра через ioctl (часто через библиотеку libdrm);
  • размещает буферы и отправляет команды GPU так, чтобы ядро могло обеспечить изоляцию процессов.
  • То есть повторяется модель из первой статьи курса:

  • user space делает вызовы библиотек;
  • библиотеки упираются в системные вызовы (open, mmap, ioctl);
  • ядро выполняет привилегированную часть работы.
  • libdrm: тонкая прослойка к DRM ioctls

    libdrm — user space библиотека, которая содержит определения структур и помощники для работы с DRM API (в основном вокруг ioctl). Она не «рисует», но делает взаимодействие с DRM удобнее и стабильнее.

    Репозиторий и описание проекта: libdrm (freedesktop.org)

    Драйверы GPU: где они на самом деле

    Слово «драйвер GPU» в Linux почти всегда означает два слоя одновременно.

    Драйвер в ядре

    Это модуль ядра, который:

  • регистрирует DRM-устройство и создаёт /dev/dri/*;
  • управляет памятью GPU и планированием исполнения команд;
  • реализует KMS для вывода;
  • загружает firmware (если требуется) и общается с железом.
  • Проверка, какой драйвер привязан к GPU:

    Просмотр сообщений драйвера в логе ядра:

    Драйвер в Mesa (user space)

    Это реализация конкретного API поверх возможностей драйвера ядра.

    Примеры типовых связок:

  • AMD:
  • - ядро: amdgpu - OpenGL: radeonsi - Vulkan: RADV
  • Intel:
  • - ядро: i915 или xe (новее) - OpenGL: iris - Vulkan: ANV
  • NVIDIA:
  • - открытый драйвер ядра может быть nouveau (функциональность зависит от поколения); - проприетарный стек NVIDIA — отдельная экосистема (ядро + user space библиотеки).

    Для дистрибутива это означает: нельзя поставить только Mesa или только модуль ядра — нужен согласованный набор.

    Композиция и вывод: где заканчивается рендеринг и начинается «показать на экране»

    Даже если приложение отрендерило кадр, его ещё нужно доставить до дисплея.

    Типовая современная цепочка в Wayland-системе:

  • приложение рендерит в буфер (через Mesa);
  • буфер передаётся композитору;
  • композитор решает, что и как показывать:
  • - иногда композитор сам композитит (склеивает окна в один итоговый буфер); - иногда использует аппаратные planes KMS (например, отдельный overlay для видео);
  • композитор программирует KMS и делает page flip (переключение отображаемого буфера).
  • Для понимания протокола Wayland и ролей compositor/clients см.: Wayland overview

    Ввод: от /dev/input/event* до событий в приложении

    Графика почти всегда идёт вместе с вводом, потому что окна бессмысленны без клавиатуры и мыши.

    evdev: стандартный интерфейс ввода ядра

    Большинство устройств ввода в Linux представлены как evdev-устройства: файлы вида /dev/input/eventN.

  • ядро формирует события (нажатия клавиш, движение мыши, жесты тачпада);
  • user space читает эти события из файла устройства.
  • Описание интерфейса: evdev (Linux input event interface)

    Проверить, какие устройства ввода есть в системе:

    libinput: нормализация и политика обработки ввода

    libinput — библиотека user space, которая:

  • читает evdev события;
  • применяет настройки и «политику» (ускорение мыши, распознавание жестов, подавление дребезга);
  • отдаёт унифицированные события композитору Wayland или Xorg.
  • Документация libinput: libinput documentation

    Seats и права: почему обычному пользователю не дают читать /dev/input/event*

    Устройства ввода чувствительны: если любое приложение сможет читать /dev/input/event*, оно сможет перехватывать пароли.

    Поэтому типичная архитектура такая:

  • доступ к устройствам ввода получает доверенный компонент сессии (Wayland-композитор или Xorg);
  • управление тем, какой пользователь и какая сессия имеет доступ к устройствам, делает менеджер логина.
  • В systemd-мире эту роль обычно выполняет systemd-logind:

  • он выдаёт сессии права на устройства текущего seat;
  • помогает безопасно переключать пользователей, VT, блокировку экрана.
  • Документация: systemd-logind

    Практическая диагностика: где искать причину проблем графики

    Проверка базовых предпосылок

  • Есть ли DRM-устройства:
  • Кто имеет доступ к render node:
  • Загружен ли модуль драйвера:
  • Логи ядра и systemd

  • Сообщения DRM и драйвера:
  • Ошибки при старте графической сессии (GDM/SDDM/композитор):
  • Наблюдение ioctl и файловых операций

    Если вы хотите увидеть, что процесс реально делает с /dev/dri, полезны инструменты из первой статьи:

    Это часто помогает отличить:

  • проблему прав доступа (ошибка на open);
  • проблему протокола/драйвера (ошибка на ioctl);
  • проблему выделения буферов (ошибка на mmap).
  • Что важно учесть при разработке дистрибутива

    Чтобы графика «просто работала», дистрибутиву нужны согласованные решения на нескольких уровнях:

  • Ядро и модули
  • - включён DRM/KMS для целевых GPU; - корректная загрузка firmware (часто отдельные пакеты); - включён devtmpfs, чтобы /dev/dri появлялся автоматически.
  • User space графика
  • - Mesa (и нужные драйверы Mesa); - libdrm; - Wayland compositor или Xorg, display manager (по дизайну дистрибутива).
  • Ввод и права
  • - udev правила для устройств; - systemd-logind (или альтернативная модель управления seat), чтобы безопасно выдавать доступ к GPU и вводу.
  • Политики доступа
  • - группы render/video и добавление пользователя в нужные группы; - понимание, какие узлы доступны приложениям (обычно renderD), а какие только композитору/системе (часто card).

    Краткое резюме

  • DRM — подсистема ядра для GPU, а KMS — её часть для настройки режимов дисплея и вывода кадров.
  • Связь user space с GPU — это в основном работа с /dev/dri/* через файловые дескрипторы и ioctl.
  • Mesa — основной открытый 3D-стек user space (OpenGL/Vulkan), который опирается на DRM-драйвер ядра.
  • Драйвер «GPU» обычно состоит из двух частей: модуля ядра и user space драйвера в Mesa (или альтернативном стеке).
  • Ввод идёт через /dev/input/event* (evdev), обрабатывается libinput и доставляется в приложения через compositor/Xorg, с обязательным контролем прав (часто через systemd-logind).
  • 6. X.Org и Wayland: композиторы, сессии, отладка графики

    X.Org и Wayland: композиторы, сессии, отладка графики

    В предыдущей статье мы разобрали базовый графический стек Linux: DRM/KMS в ядре, Mesa и драйверы в user space, а также ввод через evdev и роль systemd-logind в выдаче прав на устройства. Теперь соберём следующий уровень: как именно появляется графическая сессия, чем принципиально отличаются X.Org (X11) и Wayland, какую роль играет композитор, зачем нужен Xwayland и как системно отлаживать проблемы графики.

    Эта тема напрямую связана с разработкой дистрибутива: вам нужно выбрать стек (Wayland, X.Org или оба), правильно собрать зависимости, настроить запуск сессии и понимать, где искать причины “чёрного экрана”, “нет аппаратного ускорения”, “не работает ввод” и “приложение не открывает окно”.

    !Сравнение архитектуры X.Org и Wayland и точки взаимодействия с DRM/KMS и вводом

    Что такое “графическая сессия” в Linux

    Под графической сессией обычно понимают набор процессов и окружения, которые запускаются для конкретного пользователя и управляют окнами, вводом и выводом на экран.

    Минимальный состав типичной современной сессии:

  • Менеджер логина: выдаёт сессию пользователю и управляет “seat” (часто systemd-logind).
  • Компонент, который управляет устройствами ввода и выводом:
  • - в Wayland-мире это Wayland-композитор; - в X11-мире это X server (Xorg), а поверх него оконный менеджер (и часто отдельный композитный менеджер).
  • Клиентские приложения: браузер, терминал, IDE.
  • С точки зрения тем предыдущих статей:

  • Доступ к GPU и дисплею идёт через DRM-устройства в /dev/dri/*.
  • Доступ к вводу идёт через /dev/input/event*.
  • Права доступа и “владение” устройствами выдаются не “магически”, а через udev и менеджер сессий.
  • Документация по systemd-logind: systemd-logind.service

    X.Org (X11): сервер, оконный менеджер и (часто) композитор

    Базовая модель X11

    В X11 приложению не нужно знать про DRM/KMS напрямую. Оно подключается к X server (обычно процесс Xorg) и просит “создать окно”, “нарисовать”, “получить ввод”.

    Ключевые свойства модели X11:

  • X server управляет:
  • - окнами как объектами; - событиями ввода; - выводом (исторически это было гораздо сложнее и проходило эволюцию от framebuffer-драйверов до современного DDX/DRI/GLAMOR и KMS).
  • Оконный менеджер (WM) в X11 обычно является отдельным клиентом X server:
  • - он отвечает за рамки окон, перемещение, раскладку, горячие клавиши; - в современных окружениях часто это “композитный оконный менеджер”, который также делает композицию.

    Официальный сайт проекта X.Org: X.Org

    Композиция в X11

    Изначально X11 мог выводить окна “непосредственно” на экран, без композиции. Современные эффекты (прозрачность, тени, анимации) обычно требуют композитинга: каждое окно рисуется в отдельный буфер, а затем итоговая картинка собирается композитором.

    На практике в X11 распространены два варианта:

  • Оконный менеджер сам является композитором (часто в больших DE).
  • Есть отдельный композитный менеджер.
  • Важно: даже если у вас есть композитор, архитектурно X11 всё равно остаётся протоколом “клиенты <-> X server”, а оконный менеджер и композитор часто просто “особые клиенты”.

    Где в X11 живёт аппаратное ускорение

    X11-ускорение обычно опирается на DRI и Mesa (это продолжение темы из прошлой статьи): приложение использует OpenGL/Vulkan, а дальше user space драйверы взаимодействуют с ядром через DRM.

    Практическое следствие:

  • Проблема “X11 не стартует” и проблема “в X11 нет ускорения” часто имеют разные причины.
  • X server может стартовать, но рендеринг будет программным (например, llvmpipe).
  • Wayland: композитор как центр графической системы

    Главная идея Wayland

    Wayland меняет роли компонентов:

  • Wayland-композитор одновременно является:
  • - display server (принимает подключения клиентов и управляет поверхностями); - композитором (собирает финальный кадр); - компонентом, который читает ввод (обычно через libinput) и маршрутизирует его.

    Wayland — это протокол, а “Wayland” в разговорной речи часто означает целую экосистему:

  • протокол и базовые библиотеки (например, libwayland-client);
  • композиторы (Mutter, KWin, Weston, Sway и другие);
  • инфраструктура буферов (shared memory, dmabuf) и синхронизации.
  • Официальный сайт Wayland: Wayland

    Почему в Wayland композитор важнее, чем “сервер” в X11

    В Wayland-композитор концентрирует ответственность:

  • Он программирует KMS (выбор режимов, page flip), то есть напрямую работает с частью DRM/KMS.
  • Он определяет политику ввода: какое окно получает фокус, кто видит события.
  • Он управляет тем, как кадры клиентов попадают на экран.
  • Это даёт типичные эффекты:

  • Меньше “лишних посредников” в сравнении со старой моделью X11.
  • Проще обеспечить изоляцию ввода (клиентам обычно не дают читать сырой ввод).
  • Логика “политик” (фокус, hotkeys, захват курсора) становится частью композитора, а не разрозненных компонентов.
  • Xwayland: запуск X11-приложений внутри Wayland-сессии

    Чтобы не ломать совместимость, в Wayland-сессии часто запускают Xwayland — специальный X server, который:

  • с точки зрения X11-приложений выглядит как обычный X server;
  • с точки зрения системы является Wayland-клиентом и отдаёт свои буферы Wayland-композитору.
  • Идея простая:

  • X11-приложение рисует “как раньше”.
  • Xwayland упаковывает результат в поверхность.
  • Wayland-композитор композитит это как обычное окно.
  • Практическое следствие для дистрибутива:

  • Можно выбрать Wayland как основной путь, но оставить поддержку X11-приложений.
  • При отладке важно отличать “проблема Wayland” от “проблема Xwayland”.
  • Запуск графики: display manager, tty, seat и права на устройства

    Display manager и выбор типа сессии

    Часто графическая сессия запускается через display manager (GDM, SDDM и т.д.). Он:

  • показывает экран логина;
  • создаёт user-сессию;
  • запускает либо Wayland-композитор, либо Xorg, в зависимости от настроек.
  • При этом реальная выдача прав на устройства и привязка к seat в современных системах обычно делается через systemd-logind.

    Что такое seat и почему это важно

    Seat — это логическая “рабочая станция”: набор устройств (GPU, мониторы, клавиатура, мышь), закреплённый за конкретной локальной сессией.

    Это нужно, чтобы:

  • безопасно выдавать доступ к /dev/input/event* и к “опасным” DRM-узлам только активной сессии;
  • поддерживать несколько локальных сессий на одной машине (реже, но концепт важен);
  • корректно работать с переключением виртуальных терминалов (VT).
  • Альтернатива logind: seatd

    Некоторые минималистичные окружения используют вместо systemd-logind отдельный менеджер seat — например seatd.

    Документация seatd: seatd

    Для разработчика дистрибутива это выбор архитектуры:

  • systemd-стек обычно проще интегрировать “целиком” (logind, udev, units).
  • минимальный стек требует более явного контроля того, кто и как получает доступ к DRM и вводу.
  • Как отличать проблемы X.Org, Wayland и драйверов

    Когда “не работает графика”, причина почти всегда находится в одном из слоёв:

  • Ядро и DRM/KMS: нет устройства, не грузится драйвер, проблемы firmware.
  • Права и сессия: нет доступа к /dev/dri/renderD или /dev/input/event, неверная роль logind/seat.
  • Display server/композитор: падает Xorg или Wayland-композитор.
  • Графические библиотеки: Mesa/GL/Vulkan не те, не тот драйвер, программный рендер.
  • Сеанс и окружение: переменные окружения, неправильный выбор backend.
  • Ниже — практические маршруты диагностики.

    Отладка: быстрый чек-лист “графика не стартует”

    Проверка устройств и драйверов (уровень ядра)

  • Проверить, есть ли DRM-узлы:
  • Проверить сообщения ядра по DRM:
  • Проверить, какой драйвер привязан к GPU:
  • Если на этом этапе нет /dev/dri/* или драйвер не загрузился, обсуждать X.Org/Wayland рано: проблема ниже.

    Проверка прав и сессии

  • Проверить группы пользователя и права на render node:
  • Проверить, есть ли сессия и кто “владеет” seat (в systemd-мире):
  • Документация переменной XDG_RUNTIME_DIR: File Hierarchy Specification

    Типичные симптомы и их смысл

  • “Клиент пишет, что нет дисплея”:
  • - не установлен WAYLAND_DISPLAY, - нет сокета, - клиент запущен вне графической user-сессии.
  • “Композитор запустился, но чёрный экран”:
  • - проблемы KMS (режимы, коннекторы), - композитор не может сделать page flip, - проблемы с правами на card*.

    Отладка X11: практические отличия

    Переменная DISPLAY и подключение к X server

    X11-клиенты подключаются по адресу, заданному переменной DISPLAY.

    Проверки:

    Если DISPLAY пустой, X11-приложения “не видят” X server.

    Быстрые утилиты для проверки ввода и событий (X11)

  • xev показывает события клавиатуры и мыши в X11.
  • Официальная страница проекта X.Org содержит документацию и исходники утилит X11: X.Org Wiki

    Xwayland как отдельная зона риска

    Если Wayland-сессия работает, но конкретные X11-приложения нет:

  • проверьте, запущен ли Xwayland (обычно видно в списке процессов);
  • проверьте логи композитора, там часто есть сообщения о запуске Xwayland;
  • убедитесь, что установлен пакет Xwayland (в минимальных сборках это частая ошибка).
  • Что важно учесть при разработке дистрибутива

    Выбор стека и совместимость

    Типовые варианты стратегии:

  • Wayland по умолчанию + Xwayland для совместимости.
  • X.Org по умолчанию (чаще нужно для старого железа, специфичных драйверов или приложений, требовательных к X11-расширениям).
  • Два режима с выбором на экране логина.
  • Пакеты и компоненты, без которых будет больно

    Набор зависит от выбранного пути, но общая логика такая:

  • База графики:
  • - ядро с DRM/KMS и нужными драйверами; - Mesa и libdrm.
  • Для Wayland:
  • - Wayland-библиотеки; - композитор (например, Weston как референс); - Xwayland (если нужна совместимость).
  • Для X.Org:
  • - xorg-server и базовые компоненты X11; - оконный менеджер/DE; - при необходимости композитный менеджер.
  • Для прав и устройств:
  • - udev; - systemd-logind или альтернативный менеджер seat.

    Референс-композитор Weston: Weston

    Политика прав на устройства

    Ошибки дистрибутива часто выглядят одинаково (“не стартует графика”), но корень бывает в политике:

  • кто имеет доступ к /dev/dri/renderD*;
  • кто имеет доступ к /dev/dri/card*;
  • кто имеет доступ к /dev/input/event*;
  • как выдаётся доступ активной сессии (logind/seat).
  • Это прямое продолжение тем про VFS/устройства/udev и про загрузку/systemd.

    Краткое резюме

  • В X11 приложения общаются с X server (Xorg), а управление окнами и часто композитинг вынесены в отдельный оконный менеджер/композитор.
  • В Wayland композитор объединяет роли display server, композитора и маршрутизатора ввода и напрямую завязан на DRM/KMS.
  • Xwayland позволяет запускать X11-приложения внутри Wayland-сессии, но добавляет отдельный слой для диагностики.
  • Для отладки важно разделять уровни: DRM/KMS и драйвер, права и seat, композитор/Xorg, Mesa и ускорение, окружение сессии.
  • Для дистрибутива ключевые решения: Wayland или X.Org по умолчанию, нужна ли совместимость через Xwayland, и какая модель управления устройствами (logind или альтернатива).
  • 7. Разработка дистрибутива: пакеты, сборка, образы, репозитории, обновления

    Разработка дистрибутива: пакеты, сборка, образы, репозитории, обновления

    Разработка дистрибутива Linux — это управление цепочкой поставки (supply chain) от исходников до системы пользователя. На предыдущих шагах курса мы разобрали, как ядро и user space взаимодействуют через системные вызовы, как устройства появляются в /dev через udev, как система загружается через bootloader и initramfs, и как устроен графический стек (DRM/KMS, Mesa, Wayland/X.Org). Теперь соберём практическую “инженерную” часть: как упаковывать компоненты в пакеты, как собирать их в образы, как устроены репозитории, подписи, каналы обновлений и как всё это влияет на надёжность загрузки и работу графики.

    !Схема конвейера: от исходников до обновлений на машине пользователя

    Из чего состоит дистрибутив как продукт

    Дистрибутив обычно включает три класса артефактов.

  • Пакеты: установка, удаление и обновление отдельных компонентов (ядро, systemd, Mesa, композитор, утилиты).
  • Образы: готовая файловая система или загрузочный носитель (ISO для установщика, raw/qcow2 для VM, образ контейнера).
  • Репозитории и политика обновлений: как вы публикуете новые версии, подписываете их и обеспечиваете предсказуемость обновления.
  • С точки зрения тем курса это напрямую связано с тем, какие бинарники и конфигурации оказываются в /usr, как они запускаются systemd, и какие узлы устройств/права (udev, logind) будут доступны графической сессии.

    Пакеты: зачем они нужны и что внутри

    Пакет — это единица поставки, которая содержит:

  • Файлы (бинарники, библиотеки, конфиги, юниты systemd, udev-правила).
  • Метаданные:
  • - имя, версия, релиз; - архитектура; - зависимости; - конфликты/замены; - скрипты установки (опционально).
  • Подписи (обычно репозиторий подписывает индексы, а иногда и сами пакеты).
  • Два мира: пакетный формат и менеджер зависимостей

    Пакетный формат и менеджер зависимостей часто идут парой.

    | Экосистема | Формат пакета | Менеджер/инструменты | Типичный подход | |---|---|---|---| | Debian/Ubuntu | .deb | dpkg, apt | Индексы репозитория + строгая модель зависимостей | | Fedora/RHEL | .rpm | rpm, dnf | Репозитории с метаданными, активная политика сборки | | Arch | tar-пакеты | pacman | Простая модель + сборочные рецепты PKGBUILD |

    Полезные входные точки в документацию:

  • Debian New Maintainers' Guide
  • Fedora Packaging Guidelines
  • Arch Linux PKGBUILD
  • Зависимости: почему “просто положить бинарник” недостаточно

    Зависимости решают две задачи.

  • Корректность запуска: приложению нужны конкретные библиотеки (libc, libstdc++, libdrm, libwayland-client), шрифты, конфиги.
  • Корректность интеграции в систему: нужны юниты systemd, права на устройства (udev rules), интеграция с логином (logind) и т.д.
  • Практический пример из графики:

  • Если вы ставите композитор Wayland, пакет должен либо зависеть от нужных библиотек ввода/графики, либо поставлять их сам.
  • Если вы поставляете Mesa, она должна соответствовать ABI библиотек и ожиданиям ядра/DRM стека (иначе получите программный рендер или падения при ioctl).
  • Скрипты установки и “побочные эффекты”

    Многие системы упаковки позволяют выполнять скрипты на стадии установки/обновления. Это мощно, но опасно.

  • Скрипты ухудшают повторяемость (в разных средах могут вести себя по-разному).
  • Скрипты усложняют откат.
  • Скрипты увеличивают риск “полу-обновлённой” системы при сбое.
  • Хорошая практика для дистрибутива: по возможности делайте интеграцию через декларативные механизмы.

  • systemd юниты в пакете вместо “ручного” запуска.
  • udev-правила в /usr/lib/udev/rules.d вместо правки /dev.
  • tmpfiles-конфиги вместо скриптов, создающих директории.
  • Документация systemd по структуре системы:

  • systemd System and Service Manager
  • Сборка пакетов: воспроизводимость, изоляция, политика

    Сборка пакетов — это превращение исходников в двоичные артефакты под вашу целевую систему.

    Сборочная среда: почему нужен chroot или контейнер

    Если собирать “прямо на рабочей машине”, вы быстро получаете неуправляемые зависимости.

    Обычно используют один из вариантов.

  • chroot: минимальная среда с контролируемым набором пакетов.
  • контейнер: схожая идея, но удобнее для CI.
  • выделенные build-VM: сильнее изоляция, удобнее масштабирование.
  • Здесь важно помнить темы из начала курса: сборка — это обычные процессы в user space, и качество сборочной среды определяет, какие syscalls, библиотеки и ABI будут “зашиты” в итоговые бинарники.

    Воспроизводимые сборки

    Воспроизводимая сборка означает: один и тот же исходный код в одной и той же среде даёт побитово одинаковый результат.

    Зачем это дистрибутиву:

  • проще аудит;
  • проще сравнение артефактов;
  • выше доверие к цепочке поставки.
  • Входная точка в тему:

  • Reproducible Builds
  • Политика патчей: upstream, downstream и долг

    Почти любой дистрибутив в какой-то момент начинает патчить upstream.

  • Upstream — оригинальный проект.
  • Downstream — ваш дистрибутив.
  • Правило инженерной экономики: каждый downstream-патч — это долг. Его нужно переносить на новые версии, тестировать, объяснять пользователям.

    Практический компромисс:

  • патчить только то, что невозможно быстро решить через конфигурацию;
  • по возможности отправлять изменения обратно в upstream;
  • вести журнал патчей и причины.
  • Сборка образов: ISO, VM, контейнеры, initramfs

    Пакеты решают “из чего состоит система”, а образ решает “как доставить систему и стартовать”.

    Типы образов и когда они нужны

  • Установочный ISO: загрузка с флешки/диска, установщик раскладывает пакеты и конфиги.
  • Live ISO: система работает без установки, полезно для диагностики и демо.
  • VM-образы:
  • - raw или qcow2 для QEMU/KVM; - удобны для тестирования релизов.
  • Образы контейнеров (OCI): доставка user space без собственного ядра.
  • Контейнеры полезны для приложений и сервисов, но не заменяют полноценный дистрибутив, если вам нужны загрузчик, initramfs, udev и графический стек.

    Корневая файловая система и базовые монтирования

    Минимальный “каркас” пользовательского пространства должен учитывать темы курса про VFS и загрузку.

  • Нужны каталоги и права (/usr, /etc, /var, /home, /run).
  • Нужны псевдо-ФС при запуске:
  • - /proc (procfs) - /sys (sysfs) - /dev (devtmpfs + udev)

    Если образ предназначен для загрузки, нужно решить, что происходит до старта PID 1.

    Initramfs как часть продукта

    Мы уже разбирали, что initramfs помогает найти и смонтировать реальный root. В контексте дистрибутива это означает:

  • initramfs нужно собирать как артефакт;
  • в него нужно включать необходимые модули ядра и утилиты;
  • его содержимое зависит от вашего дизайна:
  • - поддержка LUKS/LVM/RAID; - способ поиска root (UUID/label); - ранняя диагностика.

    Документация ядра:

  • Using initramfs
  • Загрузка, systemd и “что включать по умолчанию”

    Образ системы — это ещё и политика включённых сервисов.

  • включаете ли systemd-udevd и правила udev;
  • включаете ли systemd-logind (важно для графики и устройств ввода);
  • включаете ли сетевой менеджер;
  • что является default target (multi-user.target или graphical.target).
  • Документация:

  • systemd.special
  • systemd-logind.service
  • Репозиторий: индексы, подписи, зеркала

    Репозиторий — это место публикации пакетов и метаданных, чтобы клиентская система могла:

  • узнать, какие версии доступны;
  • вычислить зависимости;
  • скачать пакеты;
  • проверить доверие.
  • Что такое “индексы репозитория”

    Обычно репозиторий содержит:

  • сами пакеты;
  • файлы метаданных (списки пакетов, контрольные суммы, зависимости);
  • подписи этих метаданных.
  • Важно: на практике клиент чаще доверяет подписанным индексам, а не “пакетам как файлам”. Поэтому компрометация индексов критичнее, чем компрометация одного пакета.

    Подпись: корень доверия в обновлениях

    В любой модели обновлений нужен корень доверия.

  • У пользователя есть доверенный ключ (или набор ключей) репозитория.
  • Репозиторий подписывает метаданные.
  • Клиент проверяет подпись перед установкой.
  • Если вы разрабатываете дистрибутив, вам нужно определить:

  • где хранится ключ и как происходит его ротация;
  • как защищены процессы подписи;
  • как пользователь получает ключ на этапе установки.
  • Каналы и ветки: stable, testing, rolling

    Политика обновлений обычно оформляется в виде веток.

  • Stable:
  • - редкие обновления версий; - акцент на исправления и безопасность; - удобно для серверов и предсказуемой среды.
  • Testing:
  • - компромисс между свежестью и риском.
  • Rolling:
  • - постоянный поток версий; - выше требования к CI и откату.

    Нельзя сказать, что один вариант “лучше”. Но важно понимать связь с графическим стеком: rolling быстрее приносит поддержку нового железа (ядро, DRM драйверы, Mesa), но повышает риск несовместимости при обновлениях.

    Обновления: стратегии, атомарность, откат

    Обновление пакетами

    Классическая стратегия — обновление набора пакетов.

    Плюсы:

  • гибко и привычно;
  • можно обновлять выборочно.
  • Минусы:

  • возможно состояние “частично обновлено”, если прервалось;
  • откат сложнее, если нет снапшотов ФС.
  • Чтобы снизить риск, дистрибутивы используют:

  • аккуратное управление ABI и зависимостями;
  • предварительное скачивание;
  • транзакционность на уровне менеджера (в пределах возможного);
  • снапшоты (если ФС поддерживает) как часть политики.
  • Обновление образами и атомарные системы

    Другой подход — обновлять не пакеты по одному, а готовые деревья файловой системы.

  • система переключается на новую ревизию целиком;
  • проще откат;
  • меньше классов ошибок “полу-обновления”.
  • Цена:

  • сложнее инфраструктура;
  • часто нужен отдельный механизм для пользовательских пакетов.
  • Обновления безопасности

    В дистрибутиве важно различать:

  • обновления функциональности (новые версии);
  • обновления безопасности (исправления уязвимостей);
  • обновления совместимости (драйверы, firmware).
  • На практике пользователи судят дистрибутив по тому, насколько быстро и безопасно приходят обновления безопасности, особенно для ядра, OpenSSL, браузеров и графического стека.

    Практический дизайн дистрибутива: минимальный чек-лист решений

    Ниже — набор решений, который обычно приходится принять осознанно. Он связывает темы курса в единую архитектуру.

  • Базовая платформа
  • - какая libc и базовые утилиты; - какая init-система (часто systemd).
  • Ядро и драйверы
  • - какие GPU/сетевые/хранилищные драйверы должны быть в дефолтной конфигурации; - как поставляется firmware.
  • Графический стек
  • - Wayland по умолчанию или X.Org; - нужен ли Xwayland; - как выдаются права на /dev/dri и /dev/input (udev + logind).
  • Модель поставки
  • - пакеты, образы, или гибрид; - ISO-установщик или готовый VM-образ; - контейнерные образы для серверных сценариев.
  • Репозитории и доверие
  • - как устроены подписи; - какая структура веток; - какая стратегия зеркал.
  • Обновления и откат
  • - поддержка снапшотов; - правила совместимости; - политика обновлений ядра и Mesa.

    Типичные классы проблем и как они связаны с упаковкой

  • Система загрузилась, но нет графики:
  • - пакет драйверов/firmware не установлен или не попал в образ; - udev-правила/группы не те, и композитор не открывает /dev/dri/card*; - logind отсутствует или не работает, и нет доступа к устройствам ввода.
  • После обновления пропало ускорение:
  • - несовместимость версий Mesa и kernel DRM драйвера; - обновился один пакет, но “пара” осталась старой из-за политики репозитория.
  • Обновление сломало загрузку:
  • - неправильная сборка initramfs; - обновление ядра без корректного обновления загрузчика/конфигов.

    Эти проблемы редко решаются “вручную на машине пользователя”. Они лечатся улучшением сборки, зависимостей и политики репозитория.

    Краткое резюме

  • Дистрибутив — это конвейер: исходники → сборка → пакеты → репозиторий → обновления → работающая система.
  • Пакет включает файлы и метаданные; правильные зависимости и минимизация установочных скриптов повышают предсказуемость.
  • Сборка должна быть изолированной и по возможности воспроизводимой, иначе вы не контролируете результат.
  • Образы решают доставку и запуск: ISO/Live/VM/контейнеры, а для загрузки критичны initramfs и политика systemd/udev.
  • Репозиторий и подписи формируют корень доверия; ветки (stable/rolling) — это политика рисков.
  • Стратегия обновлений должна учитывать атомарность и откат, особенно для ядра и графического стека.