Архитектура Linux: три уровня системы

TL;DR: Linux — три слоя: hardware (CPU, RAM, диски) → kernel (управляет оборудованием, изолирует процессы) → user space (все программы). Ядро работает в привилегированном режиме с полным доступом к памяти. Пользовательские процессы — в ограниченном режиме. Крах процесса в user space не роняет систему.

Зачем это знать

Без понимания границы kernel/user space невозможно осмыслить:

  • Почему kill -9 работает всегда, а kill -15 — не всегда (ядро vs процесс решает)
  • Почему Docker-контейнеры делят одно ядро и чем это отличается от VM
  • Почему ошибка в nginx не роняет всю машину, а баг в драйвере — роняет
  • Зачем нужен sudo и почему обычный пользователь не может форматировать диск
  • Что такое системный вызов и почему strace показывает именно их

Три уровня

┌─────────────────────────────────────────┐
│           User Space (Ring 3)           │
│                                         │
│  nginx  bash  postgres  docker  chrome  │
│         ↕ системные вызовы ↕            │
├─────────────────────────────────────────┤
│           Kernel (Ring 0)               │
│                                         │
│  Процессы · Память · Драйверы · Сеть    │
│         ↕ аппаратные прерывания ↕       │
├─────────────────────────────────────────┤
│           Hardware                      │
│                                         │
│    CPU · RAM · Disk · NIC · GPU         │
└─────────────────────────────────────────┘

Hardware — оборудование

Нижний уровень: CPU (выполняет инструкции), RAM (хранит данные и код ядра/процессов), диски, сетевые карты, GPU. Всё остальное — программное обеспечение, которое живёт в RAM.

Kernel — ядро

Ядро — программа, постоянно находящаяся в памяти. Четыре ключевые задачи:

  • Управление процессами — решает, какой процесс получит CPU (планировщик), переключает контекст между процессами. Подробнее: process-model
  • Управление памятью — каждый процесс видит собственное виртуальное адресное пространство через MMU; ядро ведёт таблицы страниц (page tables). Процесс A не может прочитать память процесса B
  • Драйверы устройств — единый интерфейс для разного оборудования. Две сетевые карты разных производителей выглядят одинаково для пользовательских программ
  • Системные вызовы — API ядра для user space. open(), read(), write(), fork(), exec() — всё, что процесс не может сделать сам (доступ к диску, создание процесса, работа с сетью)

User space — пользовательское пространство

Все запущенные программы: от bash и nginx до Docker и вашего браузера. Каждая — процесс с PID, владельцем и ограниченным доступом к ресурсам.

User space условно делится на уровни:

  • Нижний — базовые утилиты, которые выполняют одну задачу (coreutils, системные библиотеки)
  • Средний — сервисы: DNS-кэш, логирование, сетевые службы
  • Верхний — приложения, с которыми работает пользователь: браузер, IDE, база данных

Строгих границ нет — веб-сервер одновременно и «сервис» (средний уровень), и «приложение» (верхний). Но принцип «компонент использует нижестоящий, не вышестоящий» в целом работает.

Kernel mode vs user mode

Это аппаратный механизм (Ring 0 / Ring 3 на x86), а не программная абстракция.

Kernel mode (Ring 0)User mode (Ring 3)
Доступ к памятиВся физическая RAMТолько своё виртуальное адресное пространство
Инструкции CPUВсе, включая привилегированные (hlt, работа с портами I/O, управление MMU)Безопасное подмножество
Последствия багаKernel panic — вся система падаетSegfault — падает только процесс
Кто работаетЯдро, драйверы, модули ядраВсе пользовательские программы

Kernel space и user space — про память

Эти термины описывают области памяти:

  • Kernel space — область RAM, доступная только коду, работающему в kernel mode. Здесь живут структуры ядра, таблицы страниц, буферы драйверов
  • User space — область RAM, доступная пользовательским процессам. Каждый процесс получает своё виртуальное адресное пространство

Процесс не может напрямую обратиться к kernel space — при попытке CPU генерирует исключение (segfault). Единственный легальный способ попросить ядро что-то сделать — системный вызов.

# strace показывает системные вызовы — точки перехода user → kernel → user
strace -c ls /tmp 2>&1 | head -15
# % time  calls  syscall
# ------ ------ --------
#  25.00     10  openat      # процесс просит ядро открыть файл
#  18.00      4  read        # процесс просит ядро прочитать данные
#  12.00      6  mmap        # процесс просит ядро выделить память

Изоляция процессов

Ключевое следствие разделения: крах одного процесса не ронит другие.

  • Nginx упал → PostgreSQL продолжает работать. Ядро завершит процесс nginx, освободит его ресурсы
  • Баг в драйвере GPU → kernel panic, система мертва (драйвер работает в kernel mode)

Однако процесс с достаточными привилегиями (root) может нанести ущерб: rm -rf / удалит данные не потому, что нарушена изоляция, а потому, что ядро доверяет root’у. Подробнее о привилегиях: permissions-model.

Переключение контекста

На одноядерном CPU в каждый момент работает один процесс. Иллюзия многозадачности создаётся переключением контекста: каждый процесс получает квант времени (time slice), после чего ядро переключается на следующий. Кванты настолько малы, что пользователь не замечает пауз.

Что происходит при переключении:

  1. Таймер CPU генерирует прерывание → процессор переключается в kernel mode
  2. Ядро сохраняет состояние текущего процесса (регистры CPU, указатель стека, page table)
  3. Ядро обрабатывает накопившиеся события (I/O, сетевые пакеты)
  4. Планировщик (scheduler) выбирает следующий процесс из очереди готовых
  5. Ядро загружает состояние нового процесса, переключает page table
  6. CPU возвращается в user mode → новый процесс продолжает работу

Ключевой инсайт: ядро работает между квантами процессов — во время переключения контекста. Это не отдельный «всегда работающий» процесс.

На многоядерных системах (все современные машины) несколько процессов работают параллельно — по одному на ядро. Но планировщик всё равно нужен, потому что процессов обычно больше, чем ядер CPU.

Виртуальная память

Каждый процесс «думает», что владеет всей памятью системы. На деле ядро + MMU (Memory Management Unit, аппаратный блок CPU) подменяют адреса:

  • Процесс обращается к виртуальному адресу 0x7fff1234
  • MMU через таблицу страниц (page table) преобразует его в физический адрес 0x3a5e1234
  • Если страница не в RAM (вытеснена на диск, swap) — MMU генерирует page fault, ядро подгружает страницу

Ядро ведёт отдельную page table для каждого процесса и переключает их при context switch. Это обеспечивает изоляцию: процесс A физически не может адресовать память процесса B (если ядро не настроит shared memory явно).

# Виртуальная vs физическая память процесса
ps aux | head -2
# USER  PID  %MEM  VSZ    RSS
# root  1    0.1   169284 13120
# VSZ — виртуальная (сколько «видит» процесс)
# RSS — резидентная (сколько реально в RAM)

Псевдоустройства

Ядро предоставляет user space не только syscalls, но и псевдоустройства — файлы в /dev/, за которыми стоит не железо, а программная логика ядра:

УстройствоНазначение
/dev/nullПоглощает любой вход (чёрная дыра)
/dev/zeroБесконечный поток нулевых байтов
/dev/randomКриптографически стойкие случайные данные
/dev/urandomПсевдослучайные данные (не блокирует)
/dev/fullВсегда возвращает ошибку «No space left on device»

Процесс работает с ними через обычные syscalls (open, read, write) — для программы это обычные файлы.

Потоки ядра

Ядро запускает собственные потоки (kernel threads) для внутренних задач. Они видны в ps, но работают в kernel space:

ps aux | grep '\[.*\]' | head -5
# root  2  0.0  0.0  0  0 ?  S  Jan01  0:00 [kthreadd]    # менеджер потоков ядра
# root  3  0.0  0.0  0  0 ?  I  Jan01  0:05 [kworker/0:0] # рабочий поток (обработка I/O)
# root  9  0.0  0.0  0  0 ?  S  Jan01  0:00 [ksoftirqd/0] # обработка отложенных прерываний
# root 11  0.0  0.0  0  0 ?  S  Jan01  0:00 [kblockd]     # блочный I/O (дисковые операции)

Имена в квадратных скобках [kthreadd] — потоки ядра. У них нет пользовательского адресного пространства (поэтому VSZ=0 в ps).

Как это связано с остальной KB

КонцепцияУровеньПодробнее
Процессы, PID, сигналыUser spaceprocess-model
cgroups — лимиты ресурсовKernel (enforced)cgroups
Права доступа, sudoKernel (проверяет при каждом syscall)permissions-model
systemd — PID 1, управление сервисамиUser space (init)systemd
Docker-контейнерыUser space + kernel (namespaces, cgroups)architecture

Подводные камни

Важно: граница kernel/user mode — не то же самое, что root/non-root. Root — это привилегированный пользователь, но его процессы всё равно работают в user mode. Root может попросить ядро сделать что угодно (через syscalls), но не может выполнять привилегированные инструкции CPU напрямую.

Связанные материалы

  • process-model — процессы, PID, сигналы, состояния
  • permissions-model — rwx, sudo, capabilities
  • cgroups — ограничение ресурсов на уровне ядра
  • architecture — контейнеры используют kernel напрямую (в отличие от VM)