Архитектура Linux: от ядра до прав доступа

Вводный курс, закладывающий фундамент понимания логики работы операционной системы. Вы изучите иерархию компонентов, принципы организации файлов и систему разграничения прав, необходимую для безопасного управления сервером.

Логические слои ОС: взаимодействие железа, ядра и пользовательского пространства

Логические слои ОС: взаимодействие железа, ядра и пользовательского пространства

Представьте, что ваш сервер обрабатывает 10 000 запросов в секунду. Веб-сервер Nginx читает файлы с диска, шифрует их и отправляет в сеть. Звучит как единый процесс, но на деле внутри сервера каждую миллисекунду происходит сложнейшая эстафета. Чтобы понять, как настраивать сервер под высокие нагрузки, нужно осознать один фундаментальный факт: ни одна ваша программа не имеет права трогать оборудование сервера.

Операционная система Linux построена на принципе жесткой изоляции и состоит из трех базовых слоев.

Три кита архитектуры Linux

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

Чтобы этого избежать, Linux делит мир на три уровня:

  1. Оборудование (Hardware) — физические компоненты: процессор (CPU), оперативная память (RAM), диски, сетевые карты. Они ничего не знают о файлах или сайтах, они оперируют только электрическими сигналами и байтами.
  2. Ядро (Kernel Space) — сердце операционной системы. Это привилегированная зона. Только ядро имеет прямой доступ к оборудованию. Оно решает, какой программе дать процессорное время, как распределить память и как записать биты на диск.
  3. Пользовательское пространство (User Space) — песочница, в которой работаете вы и ваши программы. Здесь живут командная оболочка (bash), системные утилиты, базы данных и ваш Nginx.

Ключевой инсайт: Всё, что вы устанавливаете, настраиваете и запускаете на сервере, работает в User Space. Программы здесь абсолютно бесправны. Они живут в иллюзии, что сервер принадлежит только им, но на деле они находятся под тотальным контролем ядра.

Системные вызовы: мост между мирами

Если программа в User Space бесправна, как Nginx умудряется отправить картинку по сети? Он просит об этом ядро.

Механизм, с помощью которого обычная программа запрашивает услугу у ядра, называется системным вызовом (syscall).

Существуют сотни системных вызовов для разных задач:

  • Нужно прочитать файл? Программа делает системный вызов read.
  • Нужно выделить память? Вызов mmap.
  • Нужно отправить данные в сеть? Вызов send.

Цена безопасности: переключение контекста

Системный вызов — это не просто функция. Когда Nginx просит ядро прочитать файл, происходит переключение контекста (context switch):

  1. Процессор приостанавливает выполнение Nginx.
  2. Процессор переключается из безопасного пользовательского режима в привилегированный режим ядра.
  3. Ядро проверяет права доступа (имеет ли Nginx право читать этот файл?).
  4. Ядро дает команду диску прочитать данные.
  5. Ядро копирует данные в память Nginx.
  6. Процессор возвращается в пользовательский режим, и Nginx продолжает работу.

Для администратора высоконагруженных систем (HighLoad) понимание системных вызовов критически важно. Переключение контекста требует времени процессора. Если ваш сервер тормозит, проблема часто кроется не в нехватке памяти, а в том, что приложение делает слишком много мелких системных вызовов, заставляя процессор постоянно «прыгать» между User Space и Kernel Space.

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

Давайте свяжем слои воедино и посмотрим, как они взаимодействуют, когда клиент запрашивает главную страницу вашего сайта:

  1. Hardware: Сетевая карта получает электрический сигнал из интернета, формирует пакет данных и отправляет аппаратное прерывание процессору.
  2. Kernel Space: Ядро Linux подхватывает прерывание, забирает пакет у сетевой карты, понимает, что это HTTP-запрос, и будит веб-сервер.
  3. User Space: Nginx получает запрос. Он понимает, что нужно отдать файл index.html. Nginx делает системный вызов open и read.
  4. Kernel Space: Ядро проверяет права доступа к файлу, идет к драйверу диска и запрашивает блоки данных.
  5. Hardware: Диск находит нужные сектора и отдает данные ядру.
  6. Kernel Space: Ядро передает содержимое файла в Nginx.
  7. User Space: Nginx формирует HTTP-ответ и делает системный вызов send, чтобы отправить его клиенту.
  8. Kernel Space: Ядро передает данные драйверу сетевой карты.
  9. Hardware: Сетевая карта отправляет сигналы обратно в интернет.

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

Иерархия файловой системы и стандарт FHS: где искать конфигурации и логи

Иерархия файловой системы и стандарт FHS: где искать конфигурации и логи

Когда веб-сервер Nginx обращается к ядру через системный вызов с просьбой «прочитать файл», он должен передать точный адрес этого файла. В операционных системах семейства Windows мы привыкли к физическому разделению: диск C:\ для системы, диск D:\ для данных. Linux работает иначе: здесь нет букв дисков. Все файлы, устройства, сетевые сокеты и даже оперативная память объединяются в единую логическую структуру, которая начинается с одной точки — корневого каталога.

Чтобы в этом огромном дереве не возникло хаоса, когда каждое приложение сохраняет свои настройки и данные где попало, был создан строгий свод правил.

Стандарт FHS и единый корень

FHS (Filesystem Hierarchy Standard) — это стандарт, определяющий структуру каталогов в UNIX-подобных операционных системах. Он гарантирует предсказуемость: администратор, впервые зашедший на сервер, всегда знает, где лежат настройки сети, а где — логи базы данных, независимо от того, использует он Ubuntu, CentOS или Debian.

Вся файловая система растет из корня, который обозначается символом / (слэш). Любой путь к файлу начинается отсюда. Даже если вы подключите к серверу пять новых жестких дисков, они не появятся как отдельные сущности, а будут «примонтированы» (встроены) в виде папок внутри этого единого дерева.

Карта администратора: ключевые директории

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

/etc — Нервная система (Конфигурации)

Здесь хранятся глобальные настройки системы и всех установленных программ. В Linux конфигурации — это почти всегда обычные текстовые файлы. Если вам нужно изменить порт, на котором работает веб-сервер, добавить пользователя или прописать адреса DNS-серверов — вы идете в /etc. Важное правило: в этой директории не должно быть исполняемых бинарных файлов.

/var — Память системы (Изменяемые данные)

Название происходит от слова variable (переменный). Сюда программы пишут данные, которые постоянно меняются в процессе их работы. Самая важная поддиректория здесь — /var/log. Именно туда сыплются журналы событий (логи) ядра, системных служб и пользовательских приложений. Также в /var хранятся кэши, базы данных (например, файлы MySQL) и файлы веб-сайтов (часто в /var/www).

/usr — Склад инструментов (Пользовательские программы)

Исторически аббревиатура расшифровывалась как User System Resources. Это самая объемная директория, содержащая установленные программы, библиотеки и документацию. Данные здесь считаются статичными (read-only) — они изменяются только при обновлении системы или установке новых пакетов. Сами исполняемые файлы (бинарники) лежат в /usr/bin (для всех пользователей) и /usr/sbin (системные утилиты для администратора).

/home и /root — Личное пространство

В /home создаются персональные папки для каждого обычного пользователя (например, /home/alice). Здесь хранятся личные скрипты, ключи SSH и локальные настройки. У суперпользователя системы (администратора) есть своя отдельная домашняя директория — /root. Она вынесена за пределы /home из соображений безопасности и надежности.

/tmp — Краткосрочная память

Директория для временных файлов. Любое приложение (и любой пользователь) может записать сюда данные, которые нужны прямо сейчас, но не понадобятся после перезагрузки. Операционная система автоматически очищает /tmp при старте сервера.

Виртуальные файловые системы: иллюзия файлов

В Linux действует фундаментальный принцип: «Всё есть файл». Ядро ОС предоставляет доступ к аппаратному обеспечению и собственным внутренним структурам так, будто это обычные текстовые документы в директориях.

Для этого существуют виртуальные файловые системы, которые не занимают места на жестком диске, а генерируются ядром в оперативной памяти на лету:

  • /dev (Devices): Здесь лежат файлы устройств. Например, ваш первый жесткий диск представлен как файл /dev/sda. Терминал, в котором вы работаете — это тоже файл в /dev. Если программа хочет отправить данные на устройство, она просто пишет в этот файл, а ядро перехватывает запись и транслирует ее в электрические сигналы для железа.
  • /proc (Processes): Окно в пространство ядра. Здесь хранятся данные о запущенных процессах и состоянии системы. Если вы прочитаете файл /proc/cpuinfo, ядро мгновенно сформирует и выдаст вам текст с характеристиками вашего процессора.

Анатомия приложения на примере Nginx

Теперь соберем картину воедино. В прошлом материале мы рассматривали, как Nginx делает системные вызовы. Но как сам Nginx располагается в системе после установки? FHS «размазывает» его компоненты по разным ветвям дерева строго по их назначению:

  1. Бинарный файл (сама программа): ложится в /usr/sbin/nginx. Это статичный исполняемый код.
  2. Конфигурация: создается папка /etc/nginx/, внутри которой лежит главный файл nginx.conf. Здесь администратор задает правила работы.
  3. Логи: создается папка /var/log/nginx/. Сюда сервер непрерывно дописывает файлы access.log (кто заходил на сайт) и error.log (какие ошибки произошли).
  4. Контент: HTML-страницы и картинки, которые Nginx будет отдавать клиентам, чаще всего размещаются в /var/www/html/.

Такое разделение позволяет легко бэкапить сервер: достаточно скопировать /etc (настройки) и /var/www (данные), игнорируя тяжелый /usr (программы всегда можно переустановить из репозитория) и временный /tmp.

Мы разобрались, где лежат конфигурации и логи. Но если все настройки лежат в виде обычных файлов в /etc, что мешает любому приложению из User Space или случайному пользователю зайти туда и переписать конфигурацию веб-сервера? Чтобы этого не произошло, ядро применяет строгую модель безопасности, проверяя права доступа при каждом системном вызове.

Модель безопасности Linux: управление владельцами, группами и правами доступа

Модель безопасности Linux: управление владельцами, группами и правами доступа

В прошлой главе мы выяснили, что конфигурация веб-сервера лежит в /etc/nginx/nginx.conf. Представьте: приложение в User Space выполняет системный вызов, пытаясь перезаписать этот файл и подменить настройки. Ядро перехватывает вызов, но как оно решает — разрешить операцию или заблокировать её с ошибкой «Отказано в доступе»?

Вся безопасность Linux держится на двух вопросах: кто запрашивает действие и какие права установлены на целевой объект.

Паспорта системы: UID и GID

Ядро ОС не понимает текстовых имен вроде «alice» или «root». Для ядра каждый субъект в системе — это число.

Когда вы вводите логин и пароль, система сверяет их и присваивает вашей сессии UID (User ID — идентификатор пользователя) и GID (Group ID — идентификатор основной группы). Главное правило: приложения не существуют сами по себе. Любой запущенный процесс всегда работает от имени какого-то пользователя и наследует его UID. Когда вы запускаете скрипт, ядро видит не «скрипт пытается открыть файл», а «пользователь с UID 1000 пытается открыть файл».

Особое место занимает пользователь с UID 0 — это root, суперпользователь. Для процессов с UID 0 ядро отключает почти все проверки прав. Они могут читать любые файлы, менять настройки сети и напрямую общаться с оборудованием.

Триада доступа: Владелец, Группа, Остальные

Поскольку в Linux «всё есть файл», каждый файл и каждая директория в иерархии FHS жестко привязаны к одному владельцу (конкретному UID) и одной группе (конкретному GID).

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

  1. Владелец (User/Owner) — совпадает ли UID процесса с UID файла?
  2. Группа (Group) — входит ли процесс в группу, которой принадлежит файл?
  3. Остальные (Others) — все остальные процессы в системе.

Для каждой из этих категорий настраивается свой независимый набор разрешений.

Чтение, Запись, Выполнение (rwx)

Набор разрешений для каждой категории состоит ровно из трех действий: r (read), w (write) и x (execute). Однако их смысл кардинально меняется в зависимости от того, применяются они к файлу или к директории.

Для обычных файлов:

  • r — позволяет прочитать содержимое файла (например, посмотреть логи).
  • w — позволяет изменить или очистить файл.
  • x — позволяет ядру запустить этот файл как программу. Без этого бита даже корректно скомпилированный бинарный файл не запустится.

Для директорий (самая частая точка путаницы):

  • r — позволяет прочитать список файлов внутри директории (команда ls).
  • w — позволяет создавать, удалять и переименовывать файлы внутри этой директории. Важно: чтобы удалить файл, нужны права на запись в директорию, где он лежит, а не на сам файл!
  • x — позволяет «войти» в директорию (команда cd) и обратиться к файлам внутри нее, если вы знаете их имена.

Цифровая запись прав (Восьмеричная система)

В конфигурациях серверов права редко пишут буквами. Используется математическая сумма, где каждому биту присвоен вес:

  • r=4r = 4
  • w=2w = 2
  • x=1x = 1

Складывая эти числа, мы получаем одну цифру от 0 до 7 для каждой категории (Владелец, Группа, Остальные).

Например, стандартные права для конфигурационных файлов — 644:

  • Владелец: 4(r)+2(w)=64 (r) + 2 (w) = 6 (чтение и запись).
  • Группа: 4(r)=44 (r) = 4 (только чтение).
  • Остальные: 4(r)=44 (r) = 4 (только чтение).

Стандартные права для директорий — 755:

  • Владелец: 4(r)+2(w)+1(x)=74 (r) + 2 (w) + 1 (x) = 7 (полный доступ).
  • Группа и Остальные: 4(r)+1(x)=54 (r) + 1 (x) = 5 (чтение списка файлов и вход в директорию, без права создания новых).

Как Nginx использует эту модель под нагрузкой

Теперь соберем всё вместе на примере веб-сервера Nginx, готовящегося к высоким нагрузкам.

Чтобы принимать HTTP-запросы из интернета, сервер должен «слушать» сетевой порт 80. Ядро Linux считает порты с номерами до 1024 привилегированными — открыть их может только процесс с UID 0 (root). Поэтому главный процесс (master process) Nginx всегда запускается от имени root.

Он читает конфигурацию из /etc/nginx/nginx.conf (файл принадлежит root, права 644), открывает порт 80 и... сбрасывает привилегии.

Master-процесс порождает рабочие процессы (worker processes), которые будут непосредственно обрабатывать тысячи запросов от пользователей. Но этим процессам ядро назначает непривилегированный UID — обычно пользователя www-data.

Что это дает? Если в коде Nginx или вашего веб-приложения найдется уязвимость, и хакер сможет выполнить свой код через рабочий процесс, этот вредоносный код будет работать с правами www-data. Хакер не сможет изменить системные настройки в /etc, не сможет прочитать пароли других пользователей и не уничтожит систему, потому что ядро заблокирует эти системные вызовы на основе модели прав.

Мы увидели, как один процесс порождает другие и меняет их права для безопасности. Но как именно ядро управляет этими процессами, распределяет между ними процессорное время и память при тысячах одновременных подключений? Это станет темой следующего шага.