Apa yang bisa menjelaskan penanganan file aneh yang jarang ini pada / di tmpfs?

14

Pada ext4partisi sistem file saya, saya dapat menjalankan kode berikut:

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

yang menghasilkan

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

Melakukan hal yang sama pada tmpfs seperti dengan:

fs="/tmp"

hasil panen

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

yang pada dasarnya berarti bahwa sesuatu yang saya harapkan hanya membaca data, menyebabkan file jarang "meledak seperti balon"?

Saya berharap itu karena dukungan yang kurang sempurna untuk file jarang di tmpfsfilesystem, dan khususnya karena ioctl FIEMAP hilang, tetapi saya tidak yakin apa yang menyebabkan perilaku ini? Bisakah Anda memberitahu saya?

humanityANDpeace
sumber
bersenandung. Ada halaman nol bersama (copy-on-write), yang dapat digunakan ketika halaman jarang perlu mmap () ed, misalnya. Jadi saya tidak yakin mengapa semua jenis membaca dari file tmpfs jarang memerlukan mengalokasikan memori nyata. lwn.net/Articles/517465 . Saya bertanya-tanya apakah ini adalah beberapa efek samping dari konversi loop untuk menggunakan io langsung, tetapi tampaknya tidak boleh ada perbedaan ketika Anda mencoba menggunakan tipe loop baru pada tmpfs. spinics.net/lists/linux-fsdevel/msg60337.html
sourcejedi
mungkin ini bisa mendapatkan jawaban jika ada di SO? just a
1
Output dari / tmp memiliki file yang berbeda Sebelum / Setelah. Apakah itu salah cetak? Sebelum: 0 / tmp / sparse100 (tanpa M di akhir) Setelah: 102400 / tmp / sparse100M (dengan trailing M).
YoMismo
@YoMismo, ya itu hanya kesalahan ketik kecil
humanityANDpeace

Jawaban:

4

Pertama, Anda tidak sendirian dalam kebingungan tentang masalah seperti ini.

Ini tidak hanya terbatas pada tmpfstetapi telah menjadi perhatian yang dikutip dengan NFSv4 .

Jika aplikasi membaca 'lubang' dalam file yang jarang, sistem file mengubah blok kosong menjadi blok "nyata" yang diisi dengan nol, dan mengembalikannya ke aplikasi.

Ketika md5sumsedang mencoba untuk memindai file secara eksplisit memilih untuk melakukan ini dalam urutan berurutan , yang masuk akal berdasarkan apa yang berusaha dilakukan md5sum.

Karena ada "lubang" pada dasarnya pada file, pembacaan berurutan ini akan (dalam beberapa situasi) menyebabkan operasi copy on write untuk mengisi file. Ini kemudian masuk ke masalah yang lebih dalam seputar apakah atau tidak fallocate()seperti yang diterapkan dalam mendukung filesystem FALLOC_FL_PUNCH_HOLE.

Untungnya, tidak hanya tmpfsmendukung ini tetapi ada mekanisme untuk "menggali" lubang kembali.

Dengan menggunakan utilitas CLI fallocatekita dapat dengan sukses mendeteksi dan menggali kembali lubang-lubang ini.

Sesuai 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.

fallocateberoperasi pada tingkat file meskipun dan ketika Anda menjalankan md5sum terhadap perangkat blok (meminta membaca berurutan) Anda tersandung pada kesenjangan yang tepat antara bagaimana fallocate()syscall harus beroperasi. Kita bisa melihat ini dalam aksi:

Dalam aksi, menggunakan contoh Anda, kami melihat yang berikut:

$ 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

Sekarang ... itu menjawab pertanyaan dasar Anda. Moto umum saya adalah "menjadi aneh" jadi saya menggali lebih dalam ...

$ 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

Anda melihat bahwa hanya tindakan melakukan yang losetupmengubah ukuran file jarang. Jadi ini menjadi kombinasi yang menarik dari mana tmpfs, mekanisme HOLE_PUNCH fallocate,, dan blokir perangkat berpotongan.

Brian Redbeard
sumber
2
Terima kasih atas jawaban anda. Saya sadar tmpfsmendukung file jarang dan punch_hole. Itulah yang membuatnya sangat membingungkan - tmpfs mendukung ini, jadi mengapa pergi dan mengisi lubang jarang ketika membaca melalui perangkat loop? losetuptidak mengubah ukuran file, tetapi itu menciptakan perangkat blok, yang pada kebanyakan sistem kemudian dipindai untuk konten seperti: apakah ada tabel partisi? apakah ada sistem file dengan UUID? saya harus membuat / dev / disk / by-uuid / symlink? Dan pembacaan tersebut telah menyebabkan bagian dari file jarang dialokasikan, karena untuk beberapa alasan misterius , tmpfs mengisi lubang pada (beberapa) bacaan.
frostschutz
1
Bisakah Anda memperjelas " pembacaan berurutan akan (dalam beberapa situasi) menyebabkan salinan pada operasi tulis seperti ", tolong? Saya ingin tahu bagaimana operasi baca akan memicu salinan tindakan tulis. Terima kasih!
roaima
Ini aneh. Di sistem saya, saya mengikuti langkah-langkah yang sama, meskipun secara manual dan tidak dalam skrip. Pertama saya membuat file 100 juta seperti OP. Lalu saya ulangi langkah-langkahnya hanya dengan file 10MB. Hasil pertama: ls -s sparse100M adalah 102400. Tetapi ls -s pada file 10MB hanya 328 blok. ??
Patrick Taylor
1
@ PatrickTaylor ~ 328K adalah tentang apa yang digunakan setelah pemindai UUID datang, tetapi Anda tidak memasukkan / md5sum perangkat loop untuk membaca penuh.
frostschutz
1
Saya sedang menggali sumber untuk modul kernel loop (dalam loop.c) dan melihat bahwa ada dua fungsi yang relevan : lo_read_simple& lo_read_transfer. Ada beberapa perbedaan kecil dalam bagaimana mereka melakukan alokasi memori tingkat rendah ... lo_read_transfersebenarnya meminta non-blocking io dari slab.h( GFP_NOIO) saat melakukan alloc_page()panggilan. lo_read_simple()di sisi lain tidak tampil alloc_page().
Brian Redbeard