По-перше, ви не самотні в загадці щодо таких питань.
Це не просто обмежувалося, tmpfs
але викликало занепокоєння, посилаючись на
NFSv4 .
Якщо програма читає «дірки» у розрідженому файлі, файлова система перетворює порожні блоки у «справжні» блоки, заповнені нулями, та повертає їх у додаток.
При md5sum
спробі сканування файлу він чітко вирішує це зробити в
послідовному порядку , що має багато сенсу, виходячи з того, що намагається зробити md5sum.
Оскільки у файлі є принципово "дірки", це послідовне читання буде (в деяких ситуаціях) викликати копію при записі, як операцію з заповнення файлу. Потім це стає більш глибоким питанням щодо того, чи fallocate()
підтримується файлова система FALLOC_FL_PUNCH_HOLE
.
На щастя, це не тільки tmpfs
підтримує це, але існує механізм, щоб "викопати" нори назад.
Використовуючи утиліту CLI, fallocate
ми можемо успішно виявити та перекопати ці нори.
Відповідно до man 1 fallocate
:
-d, --dig-holes
Detect and dig holes. This makes the file sparse in-place, without
using extra disk space. The minimum size of the hole depends on
filesystem I/O block size (usually 4096 bytes). Also, when using
this option, --keep-size is implied. If no range is specified by
--offset and --length, then the entire file is analyzed for holes.
You can think of this option as doing a "cp --sparse" and then
renaming the destination file to the original, without the need for
extra disk space.
See --punch-hole for a list of supported filesystems.
fallocate
працює на рівні файлу, але коли ви працюєте md5sum
з блоковим пристроєм (запитуючи послідовне зчитування), ви fallocate()
стикаєтеся на точний проміжок між тим, як повинен працювати syscall. Це ми можемо побачити в дії:
Діючи, використовуючи ваш приклад, ми бачимо наступне:
$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ONTGAS8L06
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ONTGAS8L06/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ sudo md5sum /dev/loop0
2f282b84e7e608d5852449ed940bfc51 /dev/loop0
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 102400 /tmp/tmp.ONTGAS8L06/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ONTGAS8L06/sparse100M
Тепер ... це відповідає на ваше основне запитання. Мій загальний девіз - "стати дивним", тому я копав далі ...
$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ZcAxvW32GY
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 516 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 512 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51 /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
Ви бачите , що тільки акт виконанняlosetup
змінює розмір розрідженого файлу. Таким чином, це стає цікавим поєднанням, де tmpfs
механізм HOLE_PUNCH fallocate
та блокові пристрої перетинаються.