在我的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
文件系统中对稀疏文件的支持不太完善,特别是因为缺少 FIEMAP ioctl,但我不确定是什么导致了这种行为?你能告诉我吗?
答案1
首先你不是一个人正在为诸如此类的问题感到困惑。
这不仅限于,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()
系统调用应如何操作之间的确切差距。我们可以看到它的实际效果:
在实际操作中,使用您的示例,我们会看到以下内容:
$ 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
和块设备相交的有趣组合。