Процесс в User Space (например: Chrome, Terminal, NodeJS) не имеет прямого доступа к устройствам. Когда ему нужно чтение/запись или доступ к привилегированным ресурсам - он делает системный вызов (System Call). Системный вызов переводит исполнение в Kernel Space, где ядро решает, что делать: задействовать файловую подсистему, сетевой стек или драйвер устройства. Драйверы общаются с аппаратурой (CPU, RAM, диски, сетевые контроллеры), часто используя прерывания и DMA. Результат через ядро возвращается в процесс.


Высокоуровневая диаграмма потока

sequenceDiagram
    participant App as User process (Chrome / Node / Terminal)
    participant Lib as C library / runtime (libc / libuv)
    participant Kernel as Kernel (scheduler, VFS, drivers)
    participant Driver as Device driver
    participant HW as Hardware (CPU, RAM, Disk, NIC)

    App->>Lib: вызов API (например read()/send())
    Lib->>Kernel: system call trap (syscall)
    Kernel->>Driver: отправляет запрос на устройство (I/O)
    Driver->>HW: управляет контроллером, запускает DMA/команду
    HW-->>Driver: прерывание/сигнал о завершении
    Driver->>Kernel: уведомляет о результатe
    Kernel->>Lib: возвращает результат системного вызова
    Lib->>App: возвращает данные/статус

Процессы (user space): что это и что внутри

Что такое процесс?

Процесс - экземпляр выполняемой программы: набор инструкций, данных, контекст исполнения (регистры), таблицы страниц, дескрипторы файлов и т.д. Примеры: Chrome (много процессов/песочниц), Terminal (терминальная сессия), NodeJS (один процесс с event-loop).

Ключевые свойства процесса:

  • Виртуальное адресное пространство. Каждый процесс видит свои виртуальные адреса; MMU + таблицы страниц отображают их на физическую память.
  • Файловые дескрипторы. Открытые файлы/сокеты представлены дескрипторами - индексы в таблице FDs.
  • Потоки (threads). Несколько потоков в одном процессе разделяют память, но имеют свои стеки и регистры.
  • Контекст процессора. Регистры, указатель инструкции (IP), стек - всё это сохраняется/восстанавливается при переключениях.

Почему процессы не имеют доступа к железу напрямую?

Чтобы защитить систему и изолировать процессы друг от друга - иначе один баг или вредоносная программа могла бы повредить систему. Доступ к "привилегированным" инструкциям и регистрам разрешён только ядру.


Системный вызов - мост между user и kernel

Что такое system call?

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

Как это выглядит внутри (упрощённо):

  1. Процесс вызывает библиотечную функцию (например read() в libc).
  2. Эта функция формирует аргументы и вызывает специальную инструкцию CPU (на x86_64 - syscall; на ARM - svc) или прерывание.
  3. CPU переключается в привилегированный режим (kernel mode), управление передаётся в точку входа ядра.
  4. Ядро проверяет аргументы, права доступа, выполняет операцию (или ставит её в очередь).
  5. Результат возвращается: ядро кладёт код ошибки/результат в регистр, выполняется sysret или аналог, происходят возврат в user mode.

Пример - упрощённый C-код:

ssize_t n = read(fd, buf, count);
// libc вызывает системный вызов read(2)

Важно:

  • Вызов read() != прямое обращение к диску: read() - это syscall, который ядро преобразует в работу с VFS/драйвером/контроллером.
  • Системные вызовы стоят дороже обычного вызова функции - переключение режимов и проверки.

Ядро (kernel): обязанности и ключевые подсистемы

Ядро - центральный контролёр системы. Его основные обязанности:

- Планирование задач (scheduler)

  • Назначение CPU разным потокам/процессам.
  • Поддержка приоритетов, квантов времени, политик real-time или fair scheduling.
  • Хранит метаданные о процессах (структура task_struct в Linux).

- Управление памятью (memory manager)

  • Виртуальная память: отображение виртуальных адресов в физические (через page tables, CR3/TTBR).
  • Подсистемы: аллокация страниц, кеширование, механизм копирования по записи (COW), swap (подкачка на диск).
  • Кеши процессора: TLB (кеш таблиц страниц), L1/L2/L3 - влияние на производительность.

- Виртуальная файловая система (VFS) и файловые системы

  • VFS абстрагирует конкретные ФС (ext4, XFS, NTFS) и предоставляет единый интерфейс для syscalls типа open/read/write.
  • Файловые операции превращаются в операции над индексными дескрипторами и блоками на диске.

- Драйверы устройств

  • Переводят высокоуровневые запросы в конкретные команды контроллерам (SATA, NVMe, NIC).
  • Управляют регистрами устройства, настройкой DMA и обработкой прерываний.

- Сетевой стек

  • Обработка пакетов (Ethernet -> IP -> TCP/UDP -> socket buffer -> syscall).
  • Поддержка очередей, бандлинга, offloading (checksum offload, TSO, etc.).

- Безопасность и менеджмент ресурсов

  • Права доступа, Capabilities, SELinux/AppArmor, seccomp (ограничение syscalls).
  • cgroups - ограничение CPU, памяти, I/O для групп процессов.

Аппаратная сторона: CPU, память, диски и I/O

- CPU

  • Выполняет инструкции; поддерживает режимы привилегий (user/kernel).
  • Прерывания (interrupts): механизм, когда устройство/таймер прерывает выполнение и вызывает обработчик в ядре.
  • Контекст переключения: CPU переключает регистры и, при смене процесса, загружает другую таблицу страниц (CR3) - это дорогостоящая операция.

- Память (RAM) и кеши

  • Кеши (L1/L2/L3) дают меньшую задержку, но ограничены по объёму.
  • MMU управляет виртуальными адресами; TLB кеширует соответствия страниц.
  • Swap используется когда RAM заканчивается - перемещение страниц на диск - дорого по латентности.

- Диски (HDD/SSD) и контроллеры

  • Данные читаются/пишутся блоками; драйвер формирует команды для контроллера.
  • SSD имеют контроллеры, управляющие NAND, wear leveling, TRIM.
  • Часто используется DMA (Direct Memory Access): устройство само записывает/читает память без участия CPU (кроме настройки).

- Сетевые карты (NIC)

  • Принимают/отправляют Ethernet-пакеты; часто поддерживают offloads и ring buffers.
  • NIC может сигнализировать ядру прерыванием или по механизму polling.

Жизненный цикл одного системного вызова

Возьмём простой пример: read(fd, buf, n).

1. Userland: приложение вызывает read() из libc.

2. Libc wrapper: кладёт номер syscall и аргументы в регистры (платформенно-зависимо).

3. Инструкция перехода в ядро: CPU выполняет syscall/svc, перевод в kernel mode.

4. Kernel entry: ядро сохраняет контекст пользователя (регистры), переключается на kernel stack.

5. Проверки: валидность fd, права доступа, блокировки, проверка буфера.

6. VFS: VFS смотрит, какой драйвер обрабатывает fd (файл/сокет/pipe).

- Если данные в page cache - ядро копирует данные в буфер и возвращает.

- Если данных нет - ядро инициирует I/O:

  • Формирует команду для драйвера.
  • Запускает I/O (драйвер отправляет команду контроллеру).
  • Если операция блокирующая - ядро ставит процесс в состояние sleep и вызывает scheduler.

7. Аппаратное выполнение: контроллер читает/пишет блоки, возможно используя DMA; по завершении генерирует прерывание.

8. IRQ handler в ядре: драйвер обрабатывает прерывание, перемещает данные в page cache / буфер, ставит проснувшиеся процессы в runqueue.

9. Возврат в процесс: scheduler ставит процесс на CPU, ядро восстанавливает контекст, возвращает результат в регистры, выполняется sysret - возвращение в user mode.

10. Userland: read() возвращает число прочитанных байт или код ошибки.


Инструменты диагностики и отладки (для разработчика/админа)

Небольшой набор "must-know" инструментов:

  • strace - трассировка системных вызовов процесса (что и когда вызывает).
  • ltrace - трассировка вызовов библиотек.
  • dmesg - лог ядра (удобно для ошибок драйверов/прерываний).
  • /proc и /sys - виртуальные файловые системы ядра; много статистики и состояния.
  • top, htop - монитор CPU/памяти/процессов.
  • iostat, vmstat, sar - статистика I/O и памяти.
  • perf, ftrace, bpftrace, eBPF - продвинутая профилировка производительности и трассировка.
  • iotop - кто нагружает диск.
  • tcpdump, wireshark - анализ сетевого трафика.

Пример использования strace:

strace -f -e trace=open,read,write -p <pid>

Показывает, какие open/read/write делает процесс.


Типичные проблемы и что их вызывает

  • Высокая задержка I/O - slow disk, swapping, проблемы с контроллером, неправильно настроенный scheduler.
  • Блокировки/дедлоки - application-level, но видны как "спящие" процессы в ожидании ресурсов.
  • Контент-конференция CPU - слишком много контекст-переключений, частые syscalls; оптимизация: агрегация вызовов, уменьшение блокировок.
  • Неправильная работа драйверов - kernel oops, dmesg сообщения, утечки памяти в kernel space.
  • Безопасность: некорректные права доступа, отсутствие seccomp/namespace - риск компрометации.

Source: Orkhan Alishov's notes