Last updated on 8 months ago
稀疏文件
稀疏文件是一种文件,其中大部分内容是空的(或者说是零填充的),但文件系统仍然为其分配了存储空间,文件大,实际占用磁盘空间少,我们 使用lseek或truncate到一个固定位置生成的“空洞文件”是不会占据真正的磁盘空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <stdio.h> #include <fcntl.h> #include <unistd.h>
int main() { int fd; off_t offset = 1024 * 1024 * 1024; char buf[1];
fd = open("sparse_file.txt", O_WRONLY | O_CREAT, 0666); if (fd == -1) { perror("open"); return 1; }
if (lseek(fd, offset - 1, SEEK_SET) == -1) { perror("lseek"); close(fd); return 1; } if (write(fd, buf, 1) != 1) { perror("write"); close(fd); return 1; }
close(fd);
printf("Sparse file created successfully.\n"); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11
| #写入后,实际磁盘 占用只有4k(8个block) ,但是size为 1073741824 root@ubuntu:/home# gcc test.c -o test && ./test Sparse file created successfully. root@ubuntu:/home# ls -sl sparse_file.txt 4 -rw-r--r-- 1 root root 1073741824 Feb 26 07:15 sparse_file.txt
root@ubuntu:/home# stat sparse_file.txt File: sparse_file.txt Size: 1073741824 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 1102149 Links: 1
|
文件打洞
上边例子中稀疏文件,是通过在空文件中的某个偏移量写入了1个字节得到的。一个文件开始并非稀疏的,它已经占用了若干的磁盘空间,可能某段范围的内容已经失效了,想把这段失效的文件部分所占用的磁盘空间还给文件系统,我们为了减小文件所占用的磁盘空间,就只能通过文件打洞 (Hole Punching)的方式将非稀疏文件转为稀疏文件,主动释放一段文件所占的物理空间。
要想达到该效果,linux 系统上有专门的函数实现 fallocate
fallocate用于预分配块到文件。对于支持fallocate系统调用的文件系统,这是通过分配块并将它们标记为未初始化的,从而快速完成的,不需要IO到数据块。这比用0填充文件要快得多。
在Linux内核v2.6.31的情况下,fallocate系统调用由btrfs、ext4、ocfs2和xfs filesystems支持。 fallocate返回的退出代码在成功时是0,而失败则是1。
1 2 3
| int fallocate(int fd, int mode, off_t offset, off_t len); mode为0,此时会将文件的[offset, offset+len)区域的内容写为0 mode为 FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE [offset, offset+len)区域中的块就会被“打洞” 回收,减少系统占用
|
假如现在有个 42k大小的文件,有数据的
1 2 3
| root@ubuntu:/home# ll -s -h hole 44K -rw-r--r-- 1 root root 42K Feb 26 07:40 hole
|
接下来我用 fallocate 回收 hole 文件 在 [4096 ,8192] 的空间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <stdio.h> #include <unistd.h> #include <fcntl.h>
#include <linux/falloc.h> #include <sys/stat.h> #include <assert.h>
int main() { int ret; int fd; fd = open("./hole", O_RDWR|O_CREAT, 0755); assert(fd != -1); ret = fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, 4096,8192); assert(ret == 0); close(fd); return 0; }
|
执行程序并看下文件大小, 可以看到, size 并没有变化,还是42k, 但是 磁盘空间变化了
1 2
| root@ubuntu:/home# ll -sh hole 40K -rw-r--r-- 1 root root 42K Feb 26 07:42 hole
|