Процесс в 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?
Это контролируемый вход в ядро. Процесс просит ядро выполнить операцию от его имени (чтение файла, создание сокета, выделение памяти и т.д.).
Как это выглядит внутри (упрощённо):
- Процесс вызывает библиотечную функцию (например read() в libc).
- Эта функция формирует аргументы и вызывает специальную инструкцию CPU (на x86_64 - syscall; на ARM - svc) или прерывание.
- CPU переключается в привилегированный режим (kernel mode), управление передаётся в точку входа ядра.
- Ядро проверяет аргументы, права доступа, выполняет операцию (или ставит её в очередь).
- Результат возвращается: ядро кладёт код ошибки/результат в регистр, выполняется 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