Điều gì có thể giải thích việc xử lý tệp thưa thớt kỳ lạ này của / trong tmpfs?


14

Trên ext4phân vùng hệ thống tập tin của tôi, tôi có thể chạy đoạn mã sau:

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

mang lại

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

Làm điều tương tự trên tmpfs như với:

fs="/tmp"

sản lượng

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

về cơ bản có nghĩa là một cái gì đó tôi dự kiến ​​chỉ đọc dữ liệu, khiến tập tin thưa thớt "nổ tung như một quả bóng bay"?

Tôi hy vọng rằng đó là do sự hỗ trợ kém hoàn hảo cho tệp thưa thớt trong tmpfshệ thống tệp và đặc biệt là do thiếu FIEMAP ioctl, nhưng tôi không chắc điều gì gây ra hành vi này? Bạn có thể cho tôi biết?


ngân nga Có một trang không được chia sẻ (sao chép khi ghi), có thể được sử dụng khi một trang thưa thớt cần phải là mmap () ed, chẳng hạn. Vì vậy, tôi không chắc tại sao mọi kiểu đọc từ tệp tmpfs thưa thớt sẽ yêu cầu phân bổ bộ nhớ thực. lwn.net/Articles/517465 . Tôi tự hỏi liệu đây có phải là một số tác dụng phụ của việc chuyển đổi vòng lặp để sử dụng io trực tiếp không, nhưng có vẻ như không có sự khác biệt nào khi bạn cố gắng sử dụng loại vòng lặp mới trên tmpfs. spinics.net/lists/linux-fsdevel/msg60337.html
sourcejedi

có lẽ điều này có thể nhận được câu trả lời nếu nó là trên SO? chỉ là một suy nghĩ

1
Đầu ra của / tmp có các tệp khác nhau Trước / Sau. Đó có phải là một lỗi đánh máy? Trước: 0 / tmp / spzzy100 (không có M ở cuối) Sau: 102400 / tmp / spzzy100M (với M theo sau).
YoMismo

@YoMismo, vâng, chỉ là một lỗi đánh máy nhỏ
nhân

Câu trả lời:


4

Trước hết bạn không đơn độc trong việc đánh đố về các loại vấn đề này.

Điều này không chỉ giới hạn tmpfsmà còn là mối quan tâm được trích dẫn với NFSv4 .

Nếu một ứng dụng đọc 'lỗ' trong một tệp thưa thớt, hệ thống tệp sẽ chuyển đổi các khối trống thành các khối "thực" chứa đầy số không và trả chúng về ứng dụng.

Khi md5sumcố gắng quét một tệp, nó rõ ràng chọn thực hiện việc này theo thứ tự tuần tự , điều này rất có ý nghĩa dựa trên những gì md5sum đang cố gắng thực hiện.

Vì có những "lỗ hổng" cơ bản trong tệp, việc đọc tuần tự này sẽ xảy ra (trong một số trường hợp) gây ra một bản sao trên thao tác ghi giống như điền vào tệp. Điều này sau đó đi vào một vấn đề sâu hơn xung quanh việc có được fallocate()triển khai trong hệ thống tập tin hay khôngFALLOC_FL_PUNCH_HOLE .

May mắn thay, không chỉ tmpfshỗ trợ điều này mà còn có một cơ chế để "đào" các lỗ hổng ra ngoài.

Sử dụng tiện ích CLI, fallocatechúng ta có thể phát hiện và đào lại những lỗ hổng này thành công.

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

fallocatehoạt động ở cấp độ tập tin mặc dù và khi bạn đang chạy md5sum trên một thiết bị khối (yêu cầu đọc tuần tự) bạn đang vấp vào khoảng cách chính xác giữa cách tòa nhà fallocate()nên hoạt động. Chúng ta có thể thấy điều này trong hành động:

Trong thực tế, sử dụng ví dụ của bạn, chúng tôi thấy như sau:

$ 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

Bây giờ ... đó là câu trả lời cho câu hỏi cơ bản của bạn. Phương châm chung của tôi là "trở nên kỳ lạ" vì vậy tôi đã đào sâu thêm ...

$ 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

Bạn thấy rằng chỉ đơn thuần là hành động thực hiện các losetupthay đổi kích thước của tập tin thưa thớt. Vì vậy, điều này trở thành một sự kết hợp thú vị của nơi tmpfs, cơ chế HOLE_PUNCH fallocatevà các thiết bị khối giao nhau.


2
Cảm ơn câu trả lời của bạn. Tôi biết tmpfshỗ trợ các tệp thưa và punch_hole. Đó là điều khiến nó trở nên khó hiểu - tmpfs hỗ trợ điều này, vậy tại sao lại đi và lấp đầy những lỗ hổng thưa thớt khi đọc qua một thiết bị vòng lặp? losetupkhông thay đổi kích thước tệp, nhưng nó tạo ra một thiết bị khối, trên hầu hết các hệ thống được quét nội dung như: có bảng phân vùng không? Có một hệ thống tập tin với UUID? Tôi có nên tạo một / dev / đĩa / by-uuid / symlink không? Và những lần đọc này đã khiến các phần của tệp thưa thớt được phân bổ, vì một số lý do bí ẩn , tmpfs lấp đầy lỗ hổng trên (một số) lần đọc.
frostschutz

1
Bạn có thể làm rõ " việc đọc tuần tự sẽ xảy ra (trong một số trường hợp) gây ra một bản sao trên ghi giống như hoạt động " không? Tôi tò mò muốn hiểu làm thế nào một hoạt động đọc sẽ kích hoạt một bản sao trên hành động ghi. Cảm ơn!
roaima

Này là số lẻ. Trên hệ thống của tôi, tôi đã làm theo các bước tương tự, mặc dù theo cách thủ công và không có trong một tập lệnh. Đầu tiên tôi đã làm một tệp 100M giống như OP. Sau đó, tôi lặp lại các bước chỉ với một tệp 10MB. Kết quả đầu tiên: ls -s sp100100M là 102400. Nhưng ls -s trên tệp 10MB chỉ có 328 khối. ??
Patrick Taylor

1
@PatrickTaylor ~ 328K là về những gì được sử dụng sau khi máy quét UUID xuất hiện, nhưng bạn đã không cat / md5sum thiết bị lặp để đọc toàn bộ.
frostschutz

1
Tôi đã đào qua nguồn cho mô-đun hạt nhân vòng lặp (in loop.c) và thấy rằng có hai hàm liên quan : lo_read_simple& lo_read_transfer. Có một số khác biệt nhỏ trong cách họ thực hiện cấp phát bộ nhớ cấp thấp ... lo_read_transferthực sự yêu cầu không chặn io từ slab.h( GFP_NOIO) trong khi thực hiện alloc_page()cuộc gọi. lo_read_simple()mặt khác là không thực hiện alloc_page().
Brian Redbeard
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.