Що може пояснити цю дивну розріджену обробку файлів / in tmpfs?


14

На своєму ext4розділі файлової системи я можу запустити наступний код:

fs="/mnt/ext4"

#create sparse 100M file on ${fs}
dd if=/dev/zero \
   of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2> /dev/null

#show its actual used size before
echo "Before:"
ls ${fs}/sparse100M -s

#setting the sparse file up as loopback and run md5sum on loopback
losetup /dev/loop0 ${fs}/sparse100M 
md5sum /dev/loop0

#show its actual used size afterwards
echo "After:"
ls ${fs}/sparse100M -s

#release loopback and remove file
losetup -d /dev/loop0
rm ${fs}/sparse100M

яка врожайність

Before:
0 sparse100M
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
After:
0 sparse100M

Виконайте те саме, що і на tmpfs, як і в:

fs="/tmp"

врожайність

Before:
0 /tmp/sparse100M
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
After:
102400 /tmp/sparse100M

що в основному означає, що те, що я очікував просто прочитати дані, призвело до того, що розріджений файл "вибухне як повітряна куля"?

Я думаю, що це через менш досконалу підтримку розрідженого файлу у tmpfsфайловій системі, зокрема, через відсутність ioctl FIEMAP, але я не впевнений, що викликає таку поведінку? Ви можете мені сказати?


гул. Існує спільна нульова сторінка (копіювати під час запису), яка може бути використана, наприклад, коли розріджена сторінка повинна бути mmap () ed. Тому я не впевнений, чому для будь-якого типу читання з розрідженого файлу tmpfs потрібно буде виділити реальну пам'ять. lwn.net/Articles/517465 . Мені було цікаво, чи це якийсь побічний ефект перетворення циклу для використання прямого io, але, схоже, не повинно бути різниці, коли ви намагаєтеся використовувати новий тип циклу на tmpfs. spinics.net/lists/linux-fsdevel/msg60337.html
sourcejedi

можливо, це може отримати відповідь, якби це було на ЗО? просто думка

1
Вихід / tmp має різні файли до / після. Це помилка? Перед: 0 / tmp / sparse100 (без M в кінці) Після: 102400 / tmp / sparse100M (із заднім M).
YoMismo

@YoMismo, та був лише трохи друкарську помилку
humanityANDpeace

Відповіді:


4

По-перше, ви не самотні в загадці щодо таких питань.

Це не просто обмежувалося, 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та блокові пристрої перетинаються.


2
Дякую за вашу відповідь. Я знаю, що tmpfsпідтримує розріджені файли та punch_hole. Ось що робить це таким заплутаним - tmpfs підтримує це, так навіщо йти і заповнювати розріджені отвори, читаючи через петлевий пристрій? losetupне змінює розмір файлу, але створює блок-пристрій, який у більшості систем сканується на вміст на зразок: чи є таблиця розділів? чи існує файлова система з UUID? я повинен створити / dev / disk / by-uuid / symlink? І ці зчитування вже спричиняють виділення частин розрідженого файлу, оскільки з якоїсь загадкової причини tmpfs заповнює отвори на (деяких) зчитуваннях.
frostschutz

1
Чи можете ви уточнити, що " послідовне читання буде (в деяких ситуаціях) викликати копію при записі, як операція ", будь ласка? Мені цікаво зрозуміти, як операція читання спровокувала б копію під час запису. Дякую!
roaima

Це дивно. У моїй системі я виконував ті самі кроки, хоча вручну та не в сценарії. Спершу я зробив файл 100М так само, як і ОП. Потім я повторив кроки лише з 10MB-файлом. Перший результат: ls -s sparse100M склав 102400. Але ls -s у файлі 10MB було лише 328 блоків. ??
Патрік Тейлор

1
@PatrickTaylor ~ 328K - це те, що використовується після сканування UUID-сканерів, але ви не котили / md5sum циклічного пристрою для повного зчитування.
frostschutz

1
Я копав джерело для модуля ядра циклу (в loop.c) і побачив, що є дві відповідні функції : lo_read_simple& lo_read_transfer. Існують деякі незначні відмінності в тому, як вони здійснюють розподіл пам'яті низького рівня ... lo_read_transferнасправді вимагає не блокувати io від slab.h( GFP_NOIO) під час виконання alloc_page()дзвінка. lo_read_simple()з іншого боку, не виконує alloc_page().
Брайан Руда борода
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.