linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Уроки
  • How it works
  • Симулятор
  • База знаний
  • Собеседование
Index
Categories
All entries
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
home/linux/kb/Файловая система/sparse-files

kb/filesystem ── Файловая система ── beginner

Sparse-файлы - дыры и apparent size

Sparse-файл имеет "дыры" - блоки которые ФС не аллоцировала. При чтении возвращают нули, но не занимают места. ls показывает apparent size, du - реальное. Используются в qcow2, бэкапах, sparse loop.

view as markdownaka: sparse-file, file-holes, apparent-size

Зачем sparse

Когда нужно создать файл "размером" 100 GB, который будет постепенно заполняться - не обязательно сразу аллоцировать 100 GB на диске. Можно создать пустой файл с логическим размером 100 GB, но физически 0 байт. По мере записи ФС аллоцирует блоки.

Применения:

  • qcow2/vmdk для VM - "тонкий" виртуальный диск
  • loop-image для FS - 10 GB файла с ext4 внутри, реально занят на 1 GB
  • БД с pre-allocated tablespace (Oracle, MS SQL)
  • Бэкапы дисков с пустыми областями - ddrescue
  • Sparse logfile - перематываемый кольцевой буфер

Как создаются дыры

Три способа:

1. Seek + write через границу

bash
dd if=/dev/zero of=big.img bs=1 count=0 seek=10G

Создаёт файл с logical size 10 GB, занимает 0 блоков. ФС не пишет нули - просто запоминает "тут дыра до позиции X".

2. truncate / ftruncate

bash
truncate -s 10G big.img

То же самое одной командой - расширяет inode-len без аллокации.

3. Удаление блоков из существующего файла (FALLOC_FL_PUNCH_HOLE)

bash
fallocate -p -o 1G -l 1G existing.dat

Удалить байты 1-2 GB из середины файла, создав дыру. Логический размер не меняется, физическое использование падает.

ls / stat / du - кто что показывает

bash
$ truncate -s 10G big.img
$ ls -lh big.img
-rw-r--r-- 1 user user 10G May  2 15:00 big.img       ← apparent (logical)
$ du -h big.img
0       big.img                                       ← actual (allocated)
$ stat big.img
  Size: 10737418240   Blocks: 0      IO Block: 4096  regular empty file
  • ls -l показывает apparent size - то что вернёт seek SEEK_END
  • du показывает disk usage в килобайтных юнитах
  • du --apparent-size или du -k --apparent-size - apparent
  • stat показывает оба: Size: (apparent) и Blocks: (×512 = bytes)

Если на диске только 5 GB свободно, а ФС "видит" файлы суммарно на 20 GB - это нормально для sparse, но опасно: при заполнении дыр можно упереться в ENOSPC внутри write().

fallocate vs sparse

Sparse - не аллоцированные блоки.

fallocate (без -p) - наоборот, резервирует блоки без записи нулей:

bash
fallocate -l 10G allocated.dat

Файл "занимает" 10 GB на диске, но содержимое - undefined garbage (ядро не зануляет). Ускорение для случаев "будем писать 10 GB последовательно":

  • Защита от фрагментации - блоки выделены подряд
  • Гарантия что write() не упрётся в ENOSPC

Если ФС поддерживает - аллокация мгновенная (без записи нулей). На ext4/xfs - да. На fat - нет (всегда пишутся нули).

Опции fallocate:

ОпцияЧто
-l SIZEразмер
-o OFFSETсмещение
-pFALLOC_FL_PUNCH_HOLE - сделать дыру
-zFALLOC_FL_ZERO_RANGE - зануление с возможным sparse
-dFALLOC_FL_DIG_HOLES - найти zero-блоки и сделать их дырами
-cFALLOC_FL_COLLAPSE_RANGE - удалить и сдвинуть
-iFALLOC_FL_INSERT_RANGE - вставить и сдвинуть

fallocate -d - уплотнить существующий файл, превратив zero-области в дыры:

bash
fallocate -d disk.img

SEEK_HOLE / SEEK_DATA

Современные ФС (ext4, xfs, btrfs, tmpfs) поддерживают seek в lseek():

  • SEEK_HOLE - найти следующую дыру
  • SEEK_DATA - найти следующий аллоцированный блок

Через cp --sparse=auto (default) копирование сохраняет дыры:

bash
cp --sparse=auto big.img copy.img         # переносит sparse, если ФС умеет
cp --sparse=always big.img copy.img       # ищет zero-области и делает дыры
cp --sparse=never big.img copy.img        # копирует "плотно", дыры заполнит нулями

Аналогично для tar, rsync, dd:

bash
rsync --sparse                            # дыры сохраняются
tar --sparse -cf backup.tar big.img
dd conv=sparse if=src of=dst              # пропускать zero-блоки

Без правильных флагов sparse 100GB-файл при копировании развернётся в честные 100 GB.

Реальные применения в проде

qcow2 для KVM

bash
qemu-img create -f qcow2 disk.qcow2 100G

qcow2 - формат с встроенным sparse + COW + chain-of-snapshots. На ext4-хосте qcow2-файл ещё и сам sparse - двойная экономия.

Loop-устройство с ФС внутри

bash
truncate -s 10G ext4.img
mkfs.ext4 ext4.img
sudo mount -o loop ext4.img /mnt/loop

Файл начинает с 0 байт, mkfs выделит метаданные ( 1% от размера), дальше расходуется по мере записи.

Backup с дырками

bash
# Прямое копирование диска с пропуском zero-блоков
dd if=/dev/sda of=backup.img conv=sparse status=progress
# Или через ddrescue
ddrescue /dev/sda backup.img backup.log

Восстановление - dd if=backup.img of=/dev/sda без conv=sparse: тогда дыры дойдут до диска как реальные нули.

Когда что-то пошло не так

  • ENOSPC при записи в "пустую" дырку - физического места не хватило на материализацию блока. Sparse экономит только пока дыры пусты.
  • du показывает огромные числа после восстановления бэкапа - копировали без --sparse=auto. Дыры заполнились нулями = реальные блоки.
  • tar распаковал sparse-файл "толстым" - нужен --sparse при создании архива и при распаковке. На GNU tar 1.30+ распаковка sparse работает автоматически если archive создан с --sparse.
  • VM-диск растёт сам по себе - guest переписывает ненулевые блоки на нули, но qcow2/ФС не знает что это zero. Решение: внутри VM периодически fstrim (для SSD-aware) или zerofree + fallocate -d.
  • fallocate падает с ENOTSUP на NFS - не все версии NFS поддерживают punch_hole. На NFSv4.2 - работает.
  • rsync разворачивает дыры в нули - --sparse вместе с -S.

Проверка что файл sparse

bash
# Соотношение allocated / apparent
python3 -c "
import os
s = os.stat('big.img')
print(f'apparent: {s.st_size}, allocated: {s.st_blocks * 512}, ratio: {s.st_blocks * 512 / s.st_size if s.st_size else 0:.2%}')
"
# Карта аллоцированных областей
filefrag -v big.img
xfs_io -c 'fiemap -v' big.img         # на любой ФС в ядре >= 2.6.36

§ команды

bash
truncate -s 10G big.img

Создать sparse-файл логического размера 10 GB, физически 0

bash
du -h --apparent-size big.img

Apparent size (как видит приложение) vs обычный du = allocated

bash
fallocate -d big.img

Найти zero-области и превратить их в дыры - уплотнение

bash
cp --sparse=auto big.img copy.img

Скопировать sparse-файл, сохранив дыры

bash
rsync --sparse src.img dst.img

rsync с распознаванием дыр - не разворачивать в нули

bash
qemu-img info disk.qcow2

Покажет virtual size и actual size - двойная sparse-индикация

bash
filefrag -v file.img

Карта extents, видны 'holes' - проверить sparse-структуру файла

§ см. также

  • block-devicesBlock devices - диски в LinuxBlock device - устройство которое читается/пишется блоками фиксированного размера (обычно 512B или 4K). Диски, SSD, NVMe - всё block devices в `/dev/`.
  • inodeInodeInode - это запись в файловой системе с метаданными и указателями на блоки данных файла. Имя файла лежит отдельно (в директории) и просто указывает на inode.
  • mount-and-fstabmount и /etc/fstab - подключение ФС`mount` подключает блочное устройство или ФС к точке монтирования в дереве. `/etc/fstab` - список того что монтировать при загрузке.
  • filesystemsФайловые системы: ext4, xfs, btrfs, zfsext4 - дефолт, надёжный. xfs - для больших файлов и parallel I/O. btrfs/zfs - снапшоты, чексуммы, RAID встроенный, но сложнее.
  • lvmLVM - Logical Volume ManagerLVM - слой между [[block-devices]] и ФС: объединяет диски в pool'ы и нарезает логические тома любого размера, которые можно расширять, снапшотить, мигрировать вживую.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки