本文转载自微信公众号「奇伢云存储」,作者奇伢 。转载本文请联系奇伢云存储公众号。
聊聊背景
文件还能打洞?
支持稀疏文件语义的文件系统就可以。
支持稀疏语义的文件系统有什么基本特征?
- 实现 fallocate 接口,能够满足文件空间预分配和打洞;
- 实现 fiemap 的功能,返回文件的具体物理块分配信息;
打洞是什么意思?
英文是“punch hole”,就是在保证文件其他属性不变(比如,文件大小,inode 编号,权限等等)的条件下,主动释放一段文件所占的物理空间。
关于承诺的语义?
文件系统:punch hole 成功,文件系统可能释放,也可能没释放这部分空间,此结果不对用户承诺。
程序猿:反而是程序猿要遵守承诺,一旦 puhch hole 成功,用户将不能对这部分数据做任何假设,要当它已经没了,无论它是不是真的没了。
创建实分配的文件
为了打洞,我们需要先创建一个实际占用 4M 的文件,用 dd 命令如下:
- root@ubuntu:~/temp# dd if=/dev/urandom of=./test.txt.4M bs=1M count=4
可以用 du 命令看一下实际的物理空间:
- root@ubuntu:~/temp# du -sh ./test.txt.4M
- 4.0M ./test.txt.4M
确实是 4M,再用 stat 命令看一下:
- root@ubuntu:~/temp# stat ./test.txt.4M
- File: './test.txt.4M'
- Size: 4194304 Blocks: 8192 IO Block: 4096 regular file
- Device: fc00h/64512d Inode: 1335860 Links: 1
- Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
文件 Size 4194304 字节,物理占用 Blocks 数是 8192,这里每个 Block 单位是 512 字节,所以物理占用也是 4194304 字节,刚好 4M。
文件打个洞
原材料准备好了,Go 程序怎么给文件打个洞呢 ?
关键在于 fallocate 系统调用。
这是一个跟平台强相关的系统调用,非系统兼容的,下面以 Linux 为例。
由于这个非系统兼容的,类似于这类调用,一般都是用 syscall 这个标准库,直接下发系统调用。
完整程序示例如下:
代码关键几个事项:
- 文件头部要加上 // +build linux ;
- 调用的是 syscall.Fallocate 接口;
好了,编译一下吧:
- go build -gcflags "-N -l" ./punchhole.go
把编译出的二进制 punchhole 和 test.txt.4M 这两个放在一个目录下,实验一下效果:
- root@ubuntu:~/temp# ./punchhole
- 2021/09/08 22:22:21 punch hole success.
du 看下文件结果:
- root@ubuntu:~/temp# du -sh ./test.txt.4M
- 2.0M ./test.txt.4M
嗷,确实变成了 2M,stat 再看一下:
- root@ubuntu:~/temp# stat ./test.txt.4M
- File: './test.txt.4M'
- Size: 4194304 Blocks: 4096 IO Block: 4096 regular file
- Device: fc00h/64512d Inode: 1335860 Links: 1
- Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
- Access: 2021-07-26 15:39:06.672000000 +0800
文件大小还是 4M,实际物理空间变成了 2M( 4096 * 512 ),inode 编号、权限都没变。完美,一个空洞文件就诞生了。
文件解析:
这个文件 [ 0,2M ] 的位置是空洞,不占物理空间,读出来会是 0 数据;
[ 2M,4M ] 的数据还是原来从 /dev/urandom 设备读出来的数据,占用实际物理空间;
思考题
抛出几个关键的思考问题,大家可以自行验证。
如果 punchhole 传参是非 4k 对齐的,会怎么样?
划重点:由于文件系统内部都是按照 4k 的单位管理空间的,所以非 4k 对齐的空间是释放不掉的。 punch hole 一定要注意按照 4k 对齐。
特别还要注意一点,虽然非 4k 对齐释放不掉,但是 fallocate 调用也不会报错,这点很重要。最开始就提过,文件系统没给你承诺过啥时候释放啥。
大家可以手动验证下。
文件 test.txt.4M 的 [ 0,2M ] 被打洞之后,这个区域会是什么数据?
**全 0 数据,这个是稀疏文件系统给你的语义。**这个上面也提到过了。
奇伢教你快速用 hexdump 命令看一下:
- root@ubuntu:~/temp# hexdump ./test.txt.4M|more
- 0000000 0000 0000 0000 0000 0000 0000 0000 0000
- *
- 0200000 80e3 2c11 f8d8 256b 23b5 a191 fb80 eb5e
- 0200010 f454 e3e2 cb8b 664a a893 6f5a 2df0 99dd
- 0200020 9d30 4f19 144f b4f1 f2cd 7312 c16c 719f
- 0200030 2ef7 3195 48a1 b2c0 03f1 a08a aff3 a022
- .................
- .................
看到了吗?
0x0000000 - 0x0200000 这个区域都是 0 数据。这是 16 进制表示,换算成 10 进制,就是 [ 0 ,2M ] 的区域。
大家也可以用程序去 read 验证下。
总结
总结几个关键点:
文件打洞用的是系统调用 fallocate ;
文件打洞的时候要注意 4k 对齐,不然非对齐部分释放不掉,并且不会报错;
文件系统没承诺什么,所以当没 4k 对齐的时候,虽然没释放空间,也不会报错;
程序猿要遵守承诺,一旦声明了某段空间要释放,以后不能对此空间内容做假设;
文件打洞的位置,不占物理空间,后续读是返回 0 数据;