журналируемая ФС по умолчанию — extent-деревья вместо блочных указателей и JBD2 против «полного fsck после отключения питания»the journaling default — extent trees instead of block pointers, and JBD2 against "full fsck after a power cut"
ext4 — третье поколение семейства ext, унаследовавшее от ext3 журналирование, но решившее две его главные проблемы: адресацию файла через списки блочных указателей (медленно и лимитировано для больших файлов) и обязательный полный fsck после сбоя на больших разделах. Extent-деревья описывают целые диапазоны смежных блоков одной записью, а checksums журнала позволяют безопасно откатить только незавершённые транзакции вместо сканирования всего раздела.ext4 is the third generation of the ext family, inheriting journaling from ext3 but fixing its two biggest problems: addressing a file through lists of block pointers (slow and limited for large files) and a mandatory full fsck after a crash on large partitions. Extent trees describe entire ranges of contiguous blocks with a single entry, and journal checksums allow safely rolling back only incomplete transactions instead of scanning the whole partition.
ext4 расширяет ext3, добавляя extent-адресацию, поддержку разделов до 1 ЭиБ (эксбибайт) и файлов до 16 ТиБ, отложенное выделение блоков (delayed allocation) для лучшей группировки данных на диске, чексуммы журнала и метаданных, а также наносекундные временные метки. Формат совместим «в одну сторону»: раздел ext4 можно смонтировать драйвером ext3/ext2 при отключённых новых возможностях, но не наоборот.ext4 extends ext3 by adding extent-based addressing, support for partitions up to 1 EiB (exbibyte) and files up to 16 TiB, delayed block allocation for better on-disk data grouping, journal and metadata checksums, and nanosecond timestamps. The format is compatible "one way": an ext4 partition can be mounted by an ext3/ext2 driver with the new features disabled, but not the other way around.
| параметрparameter | ext2 | ext3 | ext4 |
|---|---|---|---|
| журналированиеjournaling | — | JBD | JBD2 (+ checksums транзакций)(+ transaction checksums) |
| адресация блоковblock addressing | прямые/косвенные указателиdirect/indirect pointers | прямые/косвенные указателиdirect/indirect pointers | extent-деревьяextent trees |
| макс. размер файлаmax file size | 2 ТиБTiB | 2 ТиБTiB | 16 ТиБTiB |
| макс. размер томаmax volume size | 32 ТиБTiB | 32 ТиБTiB | 1 ЭиБEiB |
| выделение блоковblock allocation | немедленноеimmediate | немедленноеimmediate | отложенное (delalloc)delayed (delalloc) |
| временные меткиtimestamps | секундыseconds | секундыseconds | наносекундыnanoseconds |
Весь раздел ext4 разбит на block group — фиксированные по размеру диапазоны блоков, каждый со своей копией служебных структур:An entire ext4 partition is split into block groups — fixed-size ranges of blocks, each with its own copy of the bookkeeping structures:
flex_bg резервные копии размещаются реже, чем в каждой группе, но принцип избыточности сохраняется.Every block group holds a block-occupancy bitmap, an inode-occupancy bitmap, a fixed-size inode table, and the actual data. The superblock and group descriptor table are physically duplicated across several groups — with flex_bg, backups sit less frequently than in every single group, but the redundancy principle holds.Каждый inode адресует свои данные не списком отдельных блоков (как в ext2/3), а деревом extent — записей вида «начиная с логического блока N, здесь идут M физически смежных блоков»:Each inode addresses its data not with a list of individual blocks (as in ext2/3) but with an extent tree — entries of the form "starting at logical block N, here come M physically contiguous blocks":
Практическое следствие: один extent описывает произвольно длинный диапазон смежных блоков одной записью, а не одним указателем на блок, как в ext2/3. Файл в 128 МиБ, лежащий одним смежным куском, адресуется одной extent-записью вместо тысяч отдельных блочных указателей — отсюда меньше метаданных, меньше обращений к диску при последовательном чтении и меньше глубина дерева для типичных файлов.The practical consequence: one extent describes an arbitrarily long range of contiguous blocks with a single entry, not one pointer per block as in ext2/3. A 128 MiB file laid out as one contiguous chunk is addressed by a single extent entry instead of thousands of individual block pointers — hence less metadata, fewer disk accesses on sequential reads, and shallower trees for typical files.
Журнал JBD2 защищает не данные файлов по умолчанию, а метаданные — от того, чтобы сбой питания посреди многошаговой операции (например, создание файла: выделить inode + записать запись каталога + обновить bitmap) не оставил файловую систему в противоречивом состоянии:The JBD2 journal protects not file data by default but metadata — so that a power loss mid-way through a multi-step operation (say, creating a file: allocate an inode + write a directory entry + update the bitmap) doesn't leave the filesystem in a contradictory state:
| режим (data=)mode (data=) | что журналируетсяwhat's journaled | компромиссtrade-off |
|---|---|---|
| ordered (по умолчанию)(default) | только метаданные; данные гарантированно пишутся на диск раньше, чем метаданные, которые на них ссылаютсяmetadata only; data is guaranteed to be written to disk before the metadata referencing it | баланс: после сбоя не будет мусора в новых файлах на месте старых данных, но сами данные не журналируютсяa balance: no garbage from old data will appear in new files after a crash, but the data itself isn't journaled |
| journal | и метаданные, и данные — всё проходит через журналboth metadata and data — everything goes through the journal | самый безопасный режим, но каждый блок данных фактически пишется на диск дваждыthe safest mode, but every data block is effectively written to disk twice |
| writeback | только метаданные; данные пишутся без гарантии порядка относительно метаданныхmetadata only; data is written with no ordering guarantee relative to metadata | быстрее ordered, но после сбоя новый файл иногда может показать старые данные другого файлаfaster than ordered, but after a crash a new file can sometimes expose another file's old data |
При монтировании после аварийного завершения ядро (а не e2fsck) само проигрывает журнал: сканирует записи транзакций, проверяет их checksum, и останавливается на первой повреждённой или неполной — все транзакции до этой точки применяются, всё после отбрасывается. Именно поэтому обычный ребут после сбоя занимает секунды, а не проход по всему разделу: полный e2fsck нужен только тогда, когда сам журнал повреждён или файловая система помечена как «грязная» без валидного журнала для восстановления.On mount after an unclean shutdown, the kernel (not e2fsck) replays the journal itself: it scans transaction records, verifies their checksums, and stops at the first corrupted or incomplete one — everything before that point is applied, everything after is discarded. That's why an ordinary reboot after a crash takes seconds rather than a full partition scan: a full e2fsck is only needed when the journal itself is corrupted or the filesystem is marked dirty with no valid journal to recover from.
Extent-дерево, описанное выше, адресует данные файла — но каталог в ext4 тоже файл, и поиск имени внутри него устроен отдельным механизмом. По умолчанию (фича dir_index) содержимое каталога хранится как htree — хэш-дерево, спрятанное внутри обычных блоков каталога так, что старый линейный код (ext2) видит те же блоки как «пустые» и просто их пропускает:The extent tree described above addresses a file's data — but a directory in ext4 is also a file, and looking up a name inside it works through a separate mechanism. By default (the dir_index feature) a directory's contents are stored as an htree — a hash tree hidden inside ordinary directory blocks so that old linear (ext2) code sees the same blocks as "empty" and simply skips them:
Поиск имени в htree-каталоге вычисляет хэш искомого имени и использует его как ключ — по сути тот же алгоритм, что и поиск в B-дереве, только ключом служит не само имя, а его хэш. При коллизии хэшей (разные имена с одинаковым хэшем) код продолжает сканировать соседние листовые блоки в порядке дерева, пока не переберёт все записи с совпадающим хэшем.Looking up a name in an htree directory computes the hash of the sought name and uses it as a key — essentially the same algorithm as a B-tree lookup, except the key is the name's hash rather than the name itself. On a hash collision (different names with the same hash), the code keeps scanning adjacent leaf blocks in tree order until it's checked every entry with a matching hash.
Поле i_block в inode, описанное выше как «корень extent-дерева», на самом деле переиспользуется под разные вещи в зависимости от флагов самого inode — i_flags определяет, как именно интерпретировать эти 60 байт:The i_block field in the inode, described above as "the extent tree root", is actually repurposed for different things depending on the inode's own flags — i_flags determines exactly how to interpret those 60 bytes:
| i_flags битbit | значениеmeaning | эффектeffect |
|---|---|---|
| EXT4_EXTENTS_FL | данные адресуются extent-деревомdata is addressed via an extent tree | стандартный режим для ext4; без этого флага inode интерпретируется по старой ext2/3 схеме блочных указателейthe standard ext4 mode; without this flag the inode is interpreted using the old ext2/3 block-pointer scheme |
| EXT4_INLINE_DATA_FL | данные файла лежат прямо внутри inode, без отдельного блокаthe file's data lives directly inside the inode, with no separate block | для очень маленьких файлов (символические ссылки, файлы в пару десятков байт) экономит целый блок на диске и одно обращение к немуfor very small files (symlinks, files a few dozen bytes long) saves an entire disk block and one access to it |
| EXT4_IMMUTABLE_FL | файл нельзя изменять, даже root-у, без снятия флагаthe file can't be modified, even by root, without clearing the flag | выставляется/снимается через chattr +i/-i; используется для защиты системных бинарников и конфигов от случайной или вредоносной перезаписиset/cleared via chattr +i/-i; used to protect system binaries and configs from accidental or malicious overwriting |
| EXT4_APPEND_FL | запись возможна только в конец файлаwrites are only allowed at the end of the file | chattr +a — типичное применение: log-файлы, куда можно дописывать, но нельзя редактировать или обрезать уже записанноеchattr +a — a typical use: log files that can be appended to but not edited or truncated |
| EXT4_ENCRYPT_FL | содержимое и имя файла зашифрованы через fscryptthe file's contents and name are encrypted via fscrypt | выставляется автоматически при создании файла внутри каталога с активной fscrypt-политикой шифрованияset automatically when a file is created inside a directory with an active fscrypt encryption policy |
Наносекундные временные метки, заявленные в таблице выше как преимущество ext4 над ext2/3, физически не помещаются в оригинальные 32-битные поля времени, унаследованные от ext2 — недостающие биты досеканд хранятся в отдельных дополнительных полях расширенного inode (i_extra_isize), появившихся вместе с увеличением размера inode со 128 до 256 байт. Это тот случай, где новая возможность формально описывается одним пунктом в списке фич, а на деле требует расширения самой структуры на диске, а не только новой логики поверх старой.The nanosecond timestamps claimed above as an ext4 advantage over ext2/3 don't physically fit in the original 32-bit time fields inherited from ext2 — the missing sub-second bits live in separate extra fields of the extended inode (i_extra_isize), introduced alongside the inode size growing from 128 to 256 bytes. This is a case where a new capability is formally a single line in a feature list, but actually requires extending the on-disk struct itself, not just new logic layered on top of the old one.
Помимо базового набора, у ext4 есть менее часто включаемые фичи, каждая из которых меняет что-то в структурах выше, а не просто добавляет опцию монтирования:Beyond the base feature set, ext4 has less commonly enabled features, each of which changes something in the structures above rather than just adding a mount option:
| фичаfeature | что делаетwhat it does | компромиссtrade-off |
|---|---|---|
| bigalloc | каждый бит в bitmap блоков адресует не один блок, а целый кластер (степень двойки блоков) — тот же bitmap покрывает намного больший объём данныхeach bit in the block bitmap addresses not a single block but a whole cluster (a power-of-two number of blocks) — the same bitmap covers a much larger amount of data | быстрее выделение и меньше фрагментация для очень больших файлов, но минимальная единица выделения растёт — маленькие файлы расходуют место менее эффективно; фича долго оставалась экспериментальнойfaster allocation and less fragmentation for very large files, but the minimum allocation unit grows — small files waste space less efficiently; the feature stayed experimental for a long time |
| fscrypt | шифрование содержимого и имён файлов на уровне отдельного каталога, разными ключами для разных каталогов — без отдельного блочного слоя вроде dm-cryptencrypts file contents and filenames at the level of an individual directory, with different keys for different directories — with no separate block layer like dm-crypt | метаданные inode (владелец, размер, время) остаются незашифрованными; гранулярность на уровне каталога, а не всего раздела, — основа шифрования профилей в Androidinode metadata (owner, size, timestamps) stays unencrypted; granularity is per-directory, not whole-partition — the basis for profile encryption on Android |
| casefold | регистронезависимое сравнение имён внутри отдельно помеченного каталога (флаг +F), при этом само имя на диске хранится в исходном регистреcase-insensitive name comparison inside an individually flagged directory (the +F flag), while the on-disk name itself keeps its original case | нужна для совместимости с приложениями, ожидающими регистронезависимую ФС (Wine, часть Android); совместно с fscrypt заработала не сразу — первые версии не поддерживали шифрование и casefold на одном каталоге одновременноneeded for compatibility with applications expecting a case-insensitive filesystem (Wine, parts of Android); combining it with fscrypt didn't work from day one — early versions didn't support encryption and casefold on the same directory at once |
| командаcommand | что происходитwhat happens |
|---|---|
| mkfs.ext4 -O ^has_journal /dev/sdX1 | создаёт ext4-раздел без журнала (де-факто ext2 с extent-адресацией) — используется для одноразовых/временных разделов, где журналирование только замедляет записьcreates an ext4 partition with no journal (effectively ext2 with extent addressing) — used for throwaway/temporary partitions where journaling only slows writes down |
| tune2fs -l /dev/sdX1 | выводит содержимое суперблока: включённые фичи (extent, 64bit, metadata_csum), число inode, дату последней проверки — прямой взгляд на структуру без монтированияdumps the superblock contents: enabled features (extent, 64bit, metadata_csum), inode count, last check date — a direct look at the structure without mounting |
| filefrag -v file.bin | показывает реальную extent-карту файла: сколько физических фрагментов и на каких блоках — практическая демонстрация того, во что превращается «непрерывный» файл после многократной записи и фрагментацииshows a file's actual extent map: how many physical fragments and at which blocks — a practical demonstration of what a "contiguous" file turns into after repeated writes and fragmentation |
| mount -o data=journal /dev/sdX1 /mnt | переключает режим журналирования данных на самый безопасный (и самый медленный) — обычно используется на разделах с критичными к целостности данными вроже баз данных, где двойная запись оправданаswitches the data journaling mode to the safest (and slowest) — typically used on partitions with data-integrity-critical workloads like databases, where the double write is worth it |
| dumpe2fs -h /dev/sdX1 | заголовок суперблока плюс список group descriptor — видно, сколько block group физически создано и где лежат резервные копии суперблокаthe superblock header plus the group descriptor list — shows how many block groups physically exist and where the superblock's backup copies sit |
Стандартный режим data=ordered защищает от одной конкретной проблемы (утечки чужих старых данных в новый файл через несинхронизированные метаданные), но не даёт полной гарантии сохранности только что записанных данных при сбое — если приложению критична гарантия «данные точно на диске», нужен явный fsync(), а не только надежда на журнал.the standard data=ordered mode protects against one specific problem (another file's stale data leaking into a new file via unsynced metadata), but doesn't fully guarantee just-written data survives a crash — if an application needs the guarantee "the data is definitely on disk," it needs an explicit fsync(), not just faith in the journal.
Extent-адресация решает проблему представления смежных блоков одной записью, но если файл дописывается много раз вперемешку с другими файлами, extent-ов становится много и дерево растёт вглубь — filefrag на «старом» часто дописываемом файле легко покажет десятки фрагментов даже на ext4.extent-based addressing solves the problem of representing contiguous blocks with a single entry, but if a file is repeatedly appended to interleaved with other files, extents multiply and the tree grows deeper — filefrag on an "old", frequently appended file can easily show dozens of fragments even on ext4.
Функция metadata_csum защищает метаданные (inode, дескрипторы групп, каталоги) и записи журнала от повреждения — но ext4, в отличие от Btrfs или ZFS, не хранит сквозные чексуммы содержимого обычных файлов: тихое повреждение бита в середине файла данных ext4 сам по себе не обнаружит.the metadata_csum feature protects metadata (inodes, group descriptors, directories) and journal records from corruption — but unlike Btrfs or ZFS, ext4 doesn't keep end-to-end checksums of ordinary file contents: a silent bit flip in the middle of a data file won't be detected by ext4 itself.