Библиотека — Linux — файловая иерархия и FHS [X]
Шифр
linux-filesystem-hierarchy
Разделы
linux tooling
Сверено
2 сентября — в этот день утверждения статьи сверялись с первоисточниками, а не правился текст
Объём
20 657 знаков · 17 разделов · чтения около 20 минут

Linux — файловая иерархия и FHS


Обзор

Файловая иерархия Linux — это результат 50 лет эволюции UNIX, зафиксированный (с оговорками) в стандарте FHS. В отличие от FreeBSD, где иерархия определяется одним проектом и обслуживает чёткое разделение «base system vs. порты», в Linux FHS — это компромисс между десятками дистрибутивов с разной философией. Стандарт описывает то, что уже сложилось, а не то, что должно быть — FHS явно позиционирует себя как «trailing standard».

Практический смысл понимания иерархии: она определяет, где искать конфиги, куда ставить софт, какие пути жёстко закодированы в скриптах и unit-файлах, и что произойдёт при обновлении системы. Дополнительно Linux активно использует виртуальные файловые системы (/proc, /sys, cgroup fs) как основной интерфейс между userspace и ядром — это принципиальное архитектурное отличие от FreeBSD, где ту же роль выполняют sysctl и ioctl.

Архитектура

FHS 3.0: что стандартизировано и что нет

FHS 3.0 (оригинально опубликован Linux Foundation в июне 2015, переиздан FreeDesktop.org в ноябре 2025 — они взяли на себя поддержку спецификации) определяет два ортогональных измерения для классификации данных:

Shareable vs. unshareable — можно ли разделять между хостами (пример: /usr — shareable, /etc — unshareable). Static vs. variable — изменяются ли данные без вмешательства администратора (пример: /usr — static, /var — variable).

Эта матрица задаёт логику разделения:

                 shareable      unshareable
    static       /usr           /etc, /boot
    variable     /var/mail      /var/run, /var/lock

На практике это означает: /usr можно теоретически монтировать по NFS и шарить между машинами (static + shareable), а /var/run содержит PID-файлы конкретного хоста (variable + unshareable). Сегодня NFS-шаринг /usr — редкость, но принцип разделения по-прежнему определяет, почему конфиги живут в /etc, а бинарники — в /usr.

FHS стандартизирует корневые каталоги и их назначение, но не стандартизирует: внутреннюю структуру /etc (каждый дистрибутив организует это по-своему), содержимое /srv (FHS говорит «site-specific data», но не уточняет), конкретные имена файлов внутри каталогов.

Ключевые каталоги и их логика

/etc — host-specific конфигурация. Только конфиги, никаких бинарников. В Linux здесь живёт всё: от /etc/fstab до /etc/systemd/, от /etc/network/ до /etc/apt/. Нет разделения на «base system config» и «ports config» как в FreeBSD (/etc vs. /usr/local/etc) — в Linux пакетный менеджер ставит конфиги в /etc/ независимо от происхождения пакета.

/usr — основная масса установленного ПО: бинарники (/usr/bin, /usr/sbin), библиотеки (/usr/lib), хедеры (/usr/include), архитектурно-независимые данные (/usr/share). Предполагается read-only после установки. /usr/local — для софта, установленного вручную (make install), а не через пакетный менеджер. На практике в Linux /usr/local используется реже, чем в FreeBSD, потому что большинство софта ставится через пакетные менеджеры напрямую в /usr.

/var — variable data: логи (/var/log), кеши (/var/cache), спулы (/var/spool), данные приложений (/var/lib). Ключевая особенность: /var/lib/ — каноническое место для persistent state пакетов. Например, /var/lib/docker, /var/lib/postgresql, /var/lib/dpkg.

/opt — для стороннего софта, который хочет жить в своём собственном поддереве: /opt//. Типичные обитатели: проприетарные продукты (Google Chrome ставится в /opt/google/chrome), крупные standalone-приложения. Конфиги для /opt-софта идут в /etc/opt/.

/srv — данные, обслуживаемые системой: web-контент (/srv/www или /srv/http), FTP (/srv/ftp). На практике используется непоследовательно — многие ставят web root в /var/www (Debian/Ubuntu) или вообще в /home.

/run — tmpfs, монтируется в самом начале загрузки. Volatile runtime data: PID-файлы, сокеты, lock-файлы. Появился как замена /var/run, который мог быть недоступен на ранних этапах boot (когда /var — отдельный раздел). FHS 3.0 определяет /run как стандарт, /var/run — как симлинк на /run для обратной совместимости.

/tmp — временные файлы. Может быть tmpfs (и часто является — systemd монтирует tmpfs на /tmp по умолчанию). Программы не должны рассчитывать на сохранение файлов между перезагрузками. Для файлов, которые должны пережить ребут, есть /var/tmp.

/boot — ядро (vmlinuz), initramfs, конфигурация загрузчика. На EFI-системах /boot/efi или /efi содержит EFI System Partition.

UsrMerge: слияние / и /usr

Исторически бинарники жили в двух местах: «необходимые для загрузки» в /bin, /sbin, /lib (на корневом разделе) и «остальные» в /usr/bin, /usr/sbin, /usr/lib (на отдельном разделе, который мог монтироваться позже). С появлением initramfs необходимость в этом разделении отпала — initrd берёт на себя роль минимальной rescue-среды.

UsrMerge превращает /bin, /sbin, /lib, /lib64 в символические ссылки на соответствующие каталоги в /usr:

/bin   → /usr/bin
/sbin  → /usr/sbin
/lib   → /usr/lib
/lib64 → /usr/lib64

Хронология по дистрибутивам: Fedora реализовала UsrMerge в версии 17 (2012), первой среди крупных дистрибутивов. Arch Linux последовал в 2013 году. openSUSE и Mageia — примерно в то же время. Debian шёл к этому мучительно долго: Technical Committee принял решение о mandatory merged-usr для Bookworm (Debian 12) в 2021, но финализация (перенос файлов из / в /usr внутри пакетов) завершена только в Debian 13 Trixie (вышел в августе 2025). При обновлении до Trixie dpkg может выдавать предупреждения о невозможности удалить старые директории — это ожидаемо и безопасно. Ubuntu следовала за Debian, но с меньшими трениями. Слияние bin и sbin — отдельный, второй шаг: Arch сделал его сразу, ещё в июне 2013-го, сведя /bin, /sbin и /usr/sbin в один /usr/bin, а Fedora дошла до этого только в 42-й версии, где /usr/sbin стал симлинком на /usr/bin и исчез из PATH по умолчанию.

Причина сложности в Debian: в отличие от Fedora и Arch, которые сделали «flag day» (до — не merged, после — обязательно merged), Debian пытался поддерживать оба варианта одновременно, что вызвало проблемы с пакетами, собранными на merged-системе и установленными на не-merged.

Результат: на современной (2025+) системе разделение /bin vs /usr/bin не существует. Все бинарники физически в /usr/bin, а /bin — симлинк. Это важно учитывать в скриптах: #!/bin/bash продолжает работать (через симлинк), но канонический путь теперь /usr/bin/bash.

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

Linux использует виртуальные файловые системы как основной API для взаимодействия userspace и ядра. Это не данные на диске — это проекция состояния ядра в файловую иерархию, доступная через обычные read()/write().

procfs (/proc)

Старейшая виртуальная ФС Linux (унаследована от Plan 9). Монтируется на /proc и содержит два класса информации:

Per-process информация: каждый процесс представлен каталогом /proc//. Ключевые файлы: cmdline (аргументы командной строки), environ (переменные окружения), fd/ (симлинки на открытые файл-дескрипторы), maps (карта памяти процесса), status (состояние: UID, GID, память, потоки), cgroup (принадлежность к контрольным группам — см. статью 6), ns/ (привязка к namespaces — см. статью 6).

/proc/self — магический симлинк на каталог текущего процесса. /proc/thread-self — аналог для текущего треда (с ядра 3.17).

Общесистемная информация: /proc/meminfo (состояние памяти), /proc/cpuinfo (информация о CPU), /proc/loadavg (загрузка системы), /proc/mounts (смонтированные ФС), /proc/filesystems (поддерживаемые ядром типы ФС), /proc/interrupts (статистика прерываний).

/proc/sys/ — дерево runtime-параметров ядра, доступных через sysctl(8). Например, /proc/sys/net/ipv4/ip_forward управляет форвардингом пакетов. Чтение и запись в эти файлы эквивалентны вызовам sysctl <параметр> и sysctl -w (-r у sysctl(8) — это фильтр по регулярному выражению, а не чтение). Подробнее о sysctl — в статье 5 (Ядро).

Ключевой нюанс: procfs — часть stable ABI ядра, хотя документация ядра явно этого не утверждает. На практике утилиты (ps, top, free, cat /proc/meminfo) полагаются на конкретный формат файлов в /proc, и ядро поддерживает обратную совместимость. Однако точный набор полей может расширяться между версиями.

sysfs (/sys)

Введена в ядре 2.6 (2003) для решения конкретной проблемы: procfs стал свалкой для информации, не имеющей отношения к процессам. sysfs предоставляет структурированное представление device model ядра — kobjects, устройства, драйверы, шины.

Принцип организации: каждый файл в /sys содержит ровно одно значение (число или строку). Это принципиальное отличие от /proc, где файлы часто содержат таблицы с множеством полей.

Ключевые поддеревья: /sys/class/ — устройства, сгруппированные по классам (net, block, tty), /sys/block/ — блочные устройства, /sys/bus/ — шины (pci, usb, scsi), /sys/devices/ — физическая топология устройств, /sys/module/ — загруженные модули ядра и их параметры, /sys/fs/ — информация о файловых системах (включая точку монтирования cgroup2 — /sys/fs/cgroup), /sys/kernel/ — параметры ядра (mm, debug, security).

Пример: изменение яркости экрана ноутбука — запись в файл /sys/class/backlight/<device>/brightness. Определение того, является ли диск SSD — чтение /sys/block/sda/queue/rotational (0 = SSD).

sysfs — часть stable ABI ядра: документация явно гарантирует, что интерфейс не будет сломан. Однако ABI ограничивает что может появиться, а не что присутствует в данный момент — набор файлов динамически зависит от обнаруженного оборудования.

tmpfs

Файловая система полностью в оперативной памяти (и swap, если доступен). В отличие от ramfs (её предшественника), tmpfs имеет ограничение размера (по умолчанию 50% RAM) и поддерживает swapping — неиспользуемые страницы могут быть выгружены в swap.

Типичные точки монтирования: /tmp (если включён tmp.mount в systemd), /run (volatile runtime data), /dev/shm (POSIX shared memory — glibc ожидает tmpfs здесь), /sys/fs/cgroup (корень иерархии cgroup на системах с cgroups v1; при v2 — cgroup2 fs напрямую).

Потребление памяти tmpfs отображается как «Shmem» в /proc/meminfo и «Shared» в выводе free(1). Это часто сбивает с толку: tmpfs потребляет реальную память только под фактически записанные данные, а не под заявленный размер.

devtmpfs (/dev)

Автоматически создаётся ядром при загрузке и заполняется device nodes. udev (часть systemd) затем дополняет /dev: создаёт симлинки (/dev/disk/by-uuid/, /dev/disk/by-label/), устанавливает права, запускает правила. devtmpfs существует для решения chicken-and-egg проблемы: udev нуждается в /dev для работы, но /dev раньше создавался udev.

cgroup fs

Виртуальная ФС для управления control groups. В cgroups v1 каждый контроллер (cpu, memory, blkio, pids и т.д.) монтировался как отдельная ФС типа cgroup — десятки точек монтирования под /sys/fs/cgroup/. В cgroups v2 всё упростилось: единая ФС типа cgroup2 на /sys/fs/cgroup. Современные дистрибутивы (Fedora 31+, Debian 11 Bullseye+, Ubuntu 21.10+, Arch) используют cgroups v2 по умолчанию, а systemd 258 удалил поддержку v1 («legacy» и «hybrid» иерархии) целиком — cgroup2 монтируется всегда. Подробный разбор cgroups — в статье 6 (Контейнеризация).

Другие виртуальные ФС

debugfs (/sys/kernel/debug) — отладочный интерфейс ядра, не является частью stable ABI. Используется для трассировки (ftrace), eBPF, и отладки драйверов. Доступ требует CAP_SYS_ADMIN. tracefs (/sys/kernel/tracing, ранее внутри debugfs) — выделена из debugfs для разделения отладочных и трассировочных интерфейсов. securityfs (/sys/kernel/security) — интерфейс LSM (SELinux, AppArmor, Landlock — см. статью 7). pstore (/sys/fs/pstore) — persistent storage для kernel panic logs, сохраняющий данные между ребутами через NVRAM или выделенный раздел. efivarfs (/sys/firmware/efi/efivars) — доступ к переменным UEFI. Осторожно: запись может повредить прошивку некоторых систем. bpf (/sys/fs/bpf) — pinning eBPF-объектов (программ и map'ов) для персистентности между вызовами (см. статью 10).

Практика

Просмотр всех смонтированных виртуальных ФС:

mount -t proc,sysfs,tmpfs,devtmpfs,cgroup2,securityfs
# или для полного списка с параметрами
findmnt -t proc,sysfs,tmpfs,cgroup2

Быстрая диагностика системы через /proc:

# Сколько памяти доступно (MemAvailable точнее чем MemFree)
awk '/MemAvailable/ {printf "%.1f GB\n", $2/1024/1024}' /proc/meminfo

# Количество открытых файл-дескрипторов в системе vs лимит
cat /proc/sys/fs/file-nr  # <allocated> <free> <max>

# Какие ФС поддерживает ядро (nodev = не требует блочного устройства)
cat /proc/filesystems

Работа с sysfs:

# Все сетевые интерфейсы и их состояние (operstate)
for iface in /sys/class/net/*/; do
    echo "$(basename $iface): $(cat $iface/operstate 2>/dev/null)"
done

# Информация о блочном устройстве
cat /sys/block/sda/queue/scheduler   # текущий I/O scheduler
cat /sys/block/sda/queue/rotational  # 0=SSD, 1=HDD

sysctl — удобная обёртка над /proc/sys/:

# Эквивалентные операции
cat /proc/sys/net/ipv4/ip_forward
sysctl net.ipv4.ip_forward

# Временное изменение (до ребута)
sysctl -w net.ipv4.ip_forward=1
# или
echo 1 > /proc/sys/net/ipv4/ip_forward

# Постоянное — через /etc/sysctl.d/*.conf
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/99-forwarding.conf
sysctl --system  # перечитать все файлы конфигурации

Проверка UsrMerge:

# Проверить, что система merged
ls -la /bin /sbin /lib
# Ожидаемый результат на современной системе:
# /bin -> usr/bin
# /sbin -> usr/sbin
# /lib -> usr/lib

# Канонический путь к бинарнику
readlink -f /bin/bash  # /usr/bin/bash

Где живут данные tmpfs и сколько они потребляют:

df -h --type=tmpfs
# Типичный вывод:
# tmpfs    3.9G  1.2M  3.9G   1% /run
# tmpfs    3.9G     0  3.9G   0% /dev/shm
# tmpfs    3.9G   12K  3.9G   1% /tmp

Нюансы и trade-off'ы

FHS — рекомендация, не закон. Gentoo явно заявляет о несоблюдении FHS, NixOS принципиально использует другую иерархию (/nix/store/...), GoboLinux экспериментировал с альтернативной структурой. Даже «FHS-compliant» дистрибутивы расходятся в деталях: Debian кладёт web root по умолчанию в /var/www, Red Hat — в /var/www/html, FHS рекомендует /srv.

/opt vs /usr/local — вечный спор. FHS определяет /opt для add-on software packages (каждый пакет в своём поддереве) и /usr/local для locally-installed software (дублирует структуру /usr). На практике выбор часто определяется привычкой и тем, как сконфигурирован ./configure (по умолчанию --prefix=/usr/local). Крупный enterprise-софт (Oracle, IBM) предпочитает /opt, а pip install --user кладёт в ~/.local/, cargo install — в ~/.cargo/bin (installation root по умолчанию — $HOME/.cargo): третий вариант, не описанный в FHS.

tmpfs на /tmp — палка о двух концах. systemd включает tmp.mount (tmpfs на /tmp) по умолчанию. Плюс: быстрее, автоочистка при ребуте, нет фрагментации на диске. Минус: потребляет RAM/swap. Если приложение создаёт большие временные файлы (сборка пакетов, обработка видео), может неожиданно заполнить память. Решение: или отключить tmp.mount (systemctl mask tmp.mount), или установить ограничение размера через Options=size=2G в mount unit.

/proc/sys/ vs /sys/ — пересечение зон ответственности. Параметры ядра (sysctl) живут в /proc/sys/, параметры устройств и модулей — в /sys/. Но граница нечёткая: например, параметры подсистемы vm (Virtual Memory) в /proc/sys/vm/, а Transparent Huge Pages — в /sys/kernel/mm/transparent_hugepage/. Эмпирическое правило: /proc/sys/ — для networking и system-wide tunables, /sys/ — для всего, связанного с устройствами и модулями.

Безопасность виртуальных ФС. /proc раскрывает информацию обо всех процессах системы. Параметр ядра hidepid=2 (mount option для /proc) скрывает чужие /proc// каталоги от непривилегированных пользователей — полезно для shared-хостинга и контейнерных хостов. hidepid=invisible — символьный синоним той же двойки, а исключения задаются отдельной опцией монтирования gid=. С ядра 5.8 procfs понимает эти символьные имена и добавляет ещё два инструмента: hidepid=ptraceable (оно же 4) оставляет видимыми только те процессы, которые вызывающий может трассировать, а subset=pid прячет из /proc всё, что не относится к задачам. /sys/kernel/debug (debugfs) предоставляет прямой доступ к внутренним структурам ядра — на production-системах стоит ограничивать доступ или не монтировать.

efivarfs и brick-risk. Запись в /sys/firmware/efi/efivars/ может в редких случаях повредить прошивку (зафиксированы случаи с Samsung и MSI). rm -rf / с --no-preserve-root на системе с efivarfs может превратить сервер в кирпич. Вопреки распространённому мнению, systemd монтирует efivarfs на запись (добавляя лишь nosuid, noexec и nodev) — страховка живёт в ядре: все переменные, кроме списка стандартных, создаются с флагом immutable, так что снести их без chattr -i не выйдет.

Сравнение с FreeBSD

Главное архитектурное различие — в Linux нет концепции «base system». FreeBSD чётко отделяет базовую систему (/bin, /sbin, /usr/bin, /usr/sbin — собственный код FreeBSD) от стороннего ПО (/usr/local/ — всё, что из портов и пакетов, включая серверные компоненты вроде BIND и nginx). Конфиги разделены соответственно: /etc для base, /usr/local/etc для портов. В Linux такого разделения нет — пакетный менеджер ставит бинарники в /usr/bin и конфиги в /etc вне зависимости от «происхождения» пакета.

Виртуальные ФС: FreeBSD исторически минимизировала использование procfs (он опционален и deprecated с FreeBSD 7+), предпочитая sysctl(8) как основной интерфейс к ядру. В Linux procfs и sysfs — primary API, а sysctl — обёртка над /proc/sys/. Это фундаментальное различие: FreeBSD оперирует структурированными MIB-деревьями через специализированный syscall (sysctl(2)), Linux проецирует всё в файловую систему, следуя принципу «everything is a file» до логического предела.

/sys в FreeBSD — симлинк на исходники ядра (/usr/src/sys). В Linux — это sysfs, совершенно другая сущность. /run в FreeBSD отсутствует — volatile runtime data живёт в /var/run без каких-либо проблем, потому что /var обычно на том же разделе, что и /. FHS, в свою очередь, позволяет /var на отдельном разделе, что и создало потребность в /run.

UsrMerge не имеет аналога в FreeBSD, потому что FreeBSD никогда не разносила бинарники одного уровня по разным каталогам без причины — /bin содержит то, что нужно для однопользовательского режима, и это разделение имеет практический смысл, так как FreeBSD не использует initramfs.

Источники


Связанные заметки

Из библиотеки института уносят только знания. Читателей здесь не считают: посещение не регистрируется, отметок в издании не остаётся, счётчиков нет ни у одного издания фонда.

Издание пополняется и пересверяется. Текст доступен на условиях CC BY-SA 4.0 — унесённое можно передавать дальше на тех же условиях.