点击上方蓝色“飞雪无情”关注我的公众号,设个星标,第一时间看文章
在操作系统中,有一种特殊的文件,看着很大,但是实际占用磁盘的空间却很少,这种文件就是稀疏文件。
这种文件的原理其实就是标记文件字节的内容为空(可以理解为NULL,但不是0),为空的内容不会真实的占用磁盘空间,所以也称为打洞,就像就一张纸片(文件)上打了一个个洞,这些洞没有存储数据,所以不占用空间。
生成一个稀疏文件
当知道了稀疏文件的原理后,就很容易的生成一个稀疏文件了。下面我使用Go语言生成一个1GB大小的稀疏文件,但是它的实际磁盘占用空间为0。
func main() {
sparseFile, err := os.Create("sparse_file")
if err != nil {
log.Fatalln(err)
}
sparseFile.Truncate(1 << 30)
sparseFile.Close()
}
使用 Truncate
方法设置文件的大小,然后直接Close退出,就可以得到一个稀疏文件。
➜ hello ls -ls sparse_file
0 -rw-r--r-- 1073741824 Aug 22 22:19 sparse_file
通过 ls -ls
命令,可以看下这个文件的基本信息。注意这里要留意第一个数字 0
表示该文件实际占用的磁盘空间大小,是0字节。 1073741824
表示该文件的大小,是1GB。
看,对吧,一个1GB大小的文件,实际占用才0字节。
除了 ls -ls
命令,还有 du
命令可以查看占用的实际大小。
➜ hello du sparse_file
0 sparse_file
写入一些字节的稀疏文件
我们也可以创建已经写入一些字节的稀疏文件,这个使用,对于打洞的内容,使用 Seek
方法跳过即可。
func main() {
sparseFile, err := os.Create("sparse_file")
if err != nil {
log.Fatalln(err)
}
sparseFile.Truncate(1 << 30)
sparseFile.Write([]byte{0x01})
sparseFile.Seek(1<<30-1, 0)
sparseFile.Write([]byte{0x02})
sparseFile.Close()
}
以上代码同样创建了1GB大小的稀疏文件,但是只有第1个字节和最后1个字节写了内容。 Seek
是一个很好的方法,他可以让你跳来跳去,在特定的位置写入你想写入的内容,并且保持稀疏文件的特性。
哪些系统支持稀疏文件
其实现在我们用到的大多数文件系统都支持稀疏文件,比如Windows的NTFS、Linux的Ext4、MacOS的APFS都支持稀疏文件。
这里需要注意的是苹果从2016年宣布的APFS文件系统,如果你的是早期的苹果电脑,可能会不支持。
以类Unix系统为例,即使你不用Go语言,也可以很轻松的创建一个稀疏文件,比如:
➜ dd of=sparse-file bs=1k seek=5120 count=0
以上就创建了一个5M的文件,但是实际的磁盘占用空间为0。
稀疏文件的应用
了解了稀疏文件的特点,你应该可以想出一些稀疏文件的使用场景了。
没错,就是那些将来是大文件,但是现在还没那么大的文件。对于这类场景,稀疏文件再适合不过,甚至你可以创建比你的磁盘还大的稀疏文件。
比如磁盘镜像,这在虚拟机中很常用。我们想创建一个500G大小的磁盘,但是现在又不想它占用这么多空间,用多少就占用多少,那么把稀疏文件作为磁盘镜像尤其合适。
比如内存快照,这类在虚拟机中用的比较多。一个内存10GB的虚拟机,我们想把内存数据做个快照,但是内存里的数据是不固定的,但是最大只有10GB。
这时候就可以创建一个10GB的稀疏文件,把虚拟机的内存数据写到这个稀疏文件即可,在写入数据的时候,只写非零的字节数据,这样就可以减少磁盘占用。
这里和Golang的结合可以参考syscall.Mmap系统调用,你可以自己试试,这里不再举例。
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error)
小结
稀疏文件在很多特定的场景都很好用,但是它也不是完美无缺的。比如在你重写文件的时候,磁盘可能「突然」会满;复制、打包、远程传输的时候可能会丢失稀疏文件的特性。
但是不管怎么说,这是一个很好的文件特定,尤其是在磁盘镜像、快照、日志等场景,尤其合适,善于使用它们,会有意想不到的收获。
扫码关注
分享、点赞、在看就是最大的支持