Архитектура 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), после чего ядро переключается на следующий. Кванты настолько малы, что пользователь не замечает пауз.
Что происходит при переключении:
- Таймер CPU генерирует прерывание → процессор переключается в kernel mode
- Ядро сохраняет состояние текущего процесса (регистры CPU, указатель стека, page table)
- Ядро обрабатывает накопившиеся события (I/O, сетевые пакеты)
- Планировщик (scheduler) выбирает следующий процесс из очереди готовых
- Ядро загружает состояние нового процесса, переключает page table
- 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 space | process-model |
| cgroups — лимиты ресурсов | Kernel (enforced) | cgroups |
| Права доступа, sudo | Kernel (проверяет при каждом 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)