Перейти до вмісту

Файлові системи

Файлова система — найдовговічніша абстракція в операційних системах. Вона перетворює нумерований масив блоків (модуль 14) на дерево імен, яким користуються і люди, і програми.

Практичних питань, відповіді на які лежать тут, чимало. Чому видалений файл не звільнив місце? Чому du і df показують різне? Чому копіювання мільйона дрібних файлів у сотні разів повільніше за копіювання одного великого такого самого розміру? Чому контейнер запускається миттєво, хоча образ важить гігабайт?

Передумови. Блочні пристрої (модуль 14), дескриптори файлів (модуль 6), кеш сторінок (модуль 13).

У системі одночасно працюють ext4 на диску, tmpfs у пам’яті, NFS по мережі й procfs узагалі без сховища. А програма звертається до всіх них однаково.

Віртуальна файлова система — це рівень усередині ядра, який визначає спільний інтерфейс: відкрити, прочитати, записати, отримати атрибути. Кожна конкретна файлова система реалізує ці операції по-своєму, а read() лишається тим самим.

Прийом той самий, що й з драйверами: єдиний інтерфейс зверху, різні реалізації знизу. Завдяки VFS /proc і виглядає як файли, ними не будучи (модуль 6).

Будова inode: метадані, прямі вказівники та три рівні непрямих вказівників inode — усе про файл, крім його іменітип і права доступувласник, групарозмір, часові позначкилічильник жорстких посилань12 прямих вказівників48 КіБодне звернення до дисканепрямий+ 4 МіБ2 звернення до дискаподвійно непрямий+ 4 ГіБ3 звернення до дискатричі непрямий+ 4 ТіБ4 звернення до дискаблок 4 КіБ, вказівник 4 байти → 1024 вказівники на блок; межа файлу ≈ 4 ТіБ
Імені файлу в inode немає — воно живе в каталозі. Це не деталь реалізації, а те, що робить можливими жорсткі посилання.

В inode зберігається все про файл: тип, права, власник, розмір, часові позначки, лічильник посилань і розташування даних. Ідентифікують його номером у межах файлової системи.

Прямі вказівники дають доступ до початку файлу за одне звернення, а більшість файлів якраз маленькі. Великі платять додатковими зверненнями за непрямі рівні. Схема несиметрична навмисно, бо оптимізує поширений випадок.

Каталог є файлом, який містить пари «ім’я → номер inode». Звідси випливає все інше:

  • Жорстке посилання — це ще одна така пара, що вказує на той самий inode. Не копія й не ярлик, обидва імені абсолютно рівноправні. Файл існує доти, доки лічильник посилань не впаде до нуля.
  • Символьне посилання є окремим файлом, який містить шлях у вигляді тексту. Воно може вказувати в нікуди й може перетинати межі файлових систем.
  • Права на видалення файлу належать каталогу, а не самому файлу. Тому цілком можна видалити файл, у який не маєш права записати.

Як узагалі записати, де саме лежать дані файлу?

Список блоків, тобто класичний inode, добре працює для малих файлів і роздувається для великих: файлу на 10 ГіБ знадобляться мільйони записів.

Екстенти замість цього зберігають діапазон: початок і 20 000 блоків підряд. Один запис замість тисяч. Так працюють ext4, XFS і NTFS. Платимо тим, що фрагментований файл цю перевагу втрачає, бо діапазонів стає багато.

Дерева з копіюванням під час запису, як у btrfs і ZFS, не змінюють блок на місці: новий вміст пишеться в нове місце, а вказівники оновлюються знизу вгору. Звідси й майже безкоштовні знімки, бо старі блоки просто лишаються де були.

Запис файлу складається з кількох операцій: змінити inode, позначити блоки зайнятими, дописати запис у каталог, записати самі дані. Якщо живлення зникне посередині, файлова система лишиться в суперечливому стані — блоки позначені зайнятими, а посилається на них ніхто.

Перевіряти після кожного збою всю файлову систему неприйнятно, бо на багатотерабайтному томі це години.

Журнал розв’язує задачу так: спершу записати намір у журнал, а вже потім виконати. Після збою досить прочитати журнал і або доробити операції, які встигли записатися повністю, або відкинути незавершені.

Режими журналювання відрізняються тим, що саме в цей журнал потрапляє:

Режим У журналі Наслідок
writeback лише метадані найшвидший; після збою метадані цілі, а вміст файлу може бути сміттям
ordered метадані, але дані пишуться першими типовий для ext4: розумний баланс
journal і метадані, і дані найнадійніший, усе пишеться двічі

Сучасні файлові системи

Section titled “Сучасні файлові системи”

ext4 стоїть за замовчуванням у більшості дистрибутивів. Екстенти, журналювання, передбачувана поведінка. Знімків і контрольних сум даних не вміє.

XFS орієнтована на великі файли й високий паралелізм, типова для RHEL. Розширювати її можна, а зменшувати ні.

btrfs дає копіювання під час запису, знімки, контрольні суми даних, вбудований RAID, стиснення й підтоми.

ZFS робить те саме плюс бере на себе керування томами: файлова система й менеджер томів в одному. Історично сильна саме цілісністю даних.

Найважливіше в двох останніх — контрольні суми даних. ext4 і XFS перевіряють метадані, але не вміст, тож тихе пошкодження блока лишиться непоміченим. btrfs і ZFS його помітять, а за наявності надлишковості ще й виправлять.

overlayfs об’єднує кілька каталогів в один: нижні доступні тільки для читання, верхній для запису. Запис у файл із нижнього рівня спричиняє копіювання цього файлу нагору.

На цьому й побудовані контейнери (модуль 17): шари образу лежать нижніми рівнями, спільними для всіх контейнерів, а зміни конкретного контейнера потрапляють у верхній. Тому контейнер із гігабайтного образу запускається миттєво й майже не займає місця — нічого не копіюється, поки нічого не змінюється.

FUSE дозволяє реалізувати файлову систему звичайною програмою в режимі користувача. Працює вона повільніше за ядерну через перетини межі (модуль 3), зате помилка не валить систему. Так зроблені sshfs, монтування хмарних сховищ і більшість експериментальних ФС.

Класична модель Unix має три набори прав — для власника, групи й решти — по три біти r, w, x у кожному.

Для каталогу ці біти означають зовсім не те, що для файлу. r дозволяє читати список імен, w — створювати й видаляти записи, x — заходити всередину й звертатися до вмісту за іменем. Каталог із r, але без x дасть побачити імена й нічого з ними не зробити.

Є ще особливі біти. setuid змушує програму виконуватися від імені власника файлу, і саме так працює passwd. setgid робить те саме з групою. sticky у каталозі /tmp дозволяє видаляти файл лише його власникові.

ACL дають те, чого модель із трьох наборів не вміщає: окремі права конкретним користувачам. Наявність ACL видно за + у виводі ls -l.

Terminal window
stat /etc/hostname

Усе, що лежить в inode: номер, розмір, кількість блоків, права, лічильник посилань і три часові позначки.

Terminal window
df -h /; df -i /

Зайнятість у байтах і в inode окремо. Місце може скінчитися за inode при вільних терабайтах — класична ситуація на файловій системі з мільйонами дрібних файлів.

Terminal window
du -sh /var/log 2>/dev/null; df -h /var

du рахує те, на що є імена; df — реально зайняте. Різниця — це видалені, але ще відкриті файли.

Terminal window
sudo lsof +L1 2>/dev/null | head

Файли з нульовим лічильником посилань, які досі відкриті. Саме вони пояснюють, куди зникло місце після видалення журналів.

Terminal window
echo test > /tmp/a; ln /tmp/a /tmp/b; ln -s /tmp/a /tmp/c
stat -c '%i %h %n' /tmp/a /tmp/b /tmp/c; rm /tmp/a; cat /tmp/b; cat /tmp/c

Жорстке посилання й символьне поруч. Після видалення оригіналу b читається, c — ні: перше є повноцінним іменем того самого inode, друге лише зберігало шлях.

Terminal window
findmnt -t ext4,xfs,btrfs,overlay -o TARGET,SOURCE,FSTYPE,OPTIONS

Змонтовані файлові системи з опціями. У системі з контейнерами тут буде overlay.

Terminal window
filefrag -v /var/log/syslog 2>/dev/null | head -8

Екстенти конкретного файлу: скільки суцільних діапазонів він займає.

Типові помилки розуміння

Section titled “Типові помилки розуміння”

«Ім’я файлу зберігається у файлі». Ім’я лежить у каталозі, а inode його не містить. Тому одному inode і можуть відповідати кілька імен.

«Видалив файл, місце звільнилося». Звільниться воно, коли лічильник посилань стане нулем і файл при цьому ніхто не триматиме відкритим.

«Жорстке посилання — це ярлик». Це рівноправне ім’я того самого inode. Ярликом є символьне посилання: воно зберігає шлях і ламається, коли ціль зникає.

«Не маю права писати у файл, отже не зможу його видалити». Видалення змінює каталог, тож вирішують саме права на каталог.

«Журналювання захищає дані». У типовому режимі ordered журналюються метадані. Файлова система лишиться несуперечливою, а от вміст файлу після збою може виявитися неповним.

«Місце є, а записати не можу, отже файлова система зламана». Подивіться df -i: імовірно, скінчилися inode.

«btrfs і ext4 відрізняються лише швидкістю». Головна відмінність у контрольних сумах даних і знімках. ext4 не помітить тихого пошкодження вмісту.

Перевір себе

1. Де зберігається імʼя файлу?
2. Видалили журнал на 10 ГіБ, але df показує ту саму зайнятість. Чому?
3. Файл доступний тільки для читання, але ви його видалили. Як це можливо?
4. Що саме гарантує журналювання в типовому режимі ordered?
5. Місце на диску є, але створити файл не вдається. Що перевірити першим?
6. Чому контейнер із гігабайтного образу запускається миттєво?

A6 — файлова система у файлі. Власний формат із суперблоком, таблицею inode і бітовою картою блоків, змонтований через FUSE. Обов’язкова частина — реалізувати жорсткі посилання й переконатися, що видалення одного імені не знищує файл.