apue文件中的空洞是如何通过来的?
摘要:空洞的概念 linux 上普通文件的大小与占用空间是两个概念,前者表示文件中数据的长度,后者表示数据占用的磁盘空间,通常后者大于前者,因为需要一些额外的空间用来记录文件的某些统计信息或附加信息、以及切分为块的数据信息 (通常不会占用太多)。
空洞的概念
linux 上普通文件的大小与占用空间是两个概念,前者表示文件中数据的长度,后者表示数据占用的磁盘空间,通常后者大于前者,因为需要一些额外的空间用来记录文件的某些统计信息或附加信息、以及切分为块的数据信息 (通常不会占用太多)。文件占用空间也可以小于文件尺寸,此时文件内部就存在空洞了。
所谓空洞其实就是没有分配存储空间的数据块,当访问这些数据块时,系统返回 0,就如同读到空文件一般,当写这些块时,系统再实地分配对应的存储空间。其实这个和内存中的虚址地址与物理地址的概念非常相似——操作系统可以预分配一大块内存地址,这个地址只是一段连续的数字,用来保证虚拟地址不会被其它人占用,而对应的物理地址只在用到时才分配,这样就避免了一下分配一大块内存带来的浪费问题。同理,如果抽象出一个文件地址和存储地址来的话,完全可以套用上面的结论:连续的文件地址保证用户可以访问任意偏移的文件数据;文件中的空洞又避免了一下子分配太多的物理存储带来的浪费。
所以空洞不光针对文件,也可以针对内存,可以将虚址中的缺页中断理解为填补内存空洞的过程,文件中也有类似的机制。不过也有一些差异,例如内存因进程间共享而引入的 copy-on-write 机制,文件中就没有。文件同一地址的数据如果被多个进程同时写入时,只有最后一个写入的会生效,前面的那些都会被覆盖,因为文件是系统级别的概念,不像内存一样专属于某个进程。
空洞的产生
下面分平台说明。
Linux
所有的类 Unix 系统都差不多,方法比较简单,满足以下两点即可:
设置文件的偏移量 (lseek) 超过文件尾端
并写了某些数据后 (write)
此时原文件末尾到新文件末尾之间将标记为空洞。甚至都不需要写一个程序,就可以验证:
$ echo "this is a test" > test.txt
$ ls -lh test.txt
-rw-rw-r-- 1 yunh yunh 15 Oct 30 16:14 test.txt
$ stat test.txt
File: test.txt
Size: 15 Blocks: 8 IO Block: 4096 regular file
Device: 805h/2053d Inode: 35259462 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ yunh) Gid: ( 1000/ yunh)
Access: 2021-10-30 16:15:00.767760242 +0800
Modify: 2021-10-30 16:14:58.160147599 +0800
Change: 2021-10-30 16:14:58.160147599 +0800
Birth: -
$ du -sh test.txt
4.0K test.txt
$ truncate -s 1M test.txt
$ ls -lh test.txt
-rw-rw-r-- 1 yunh yunh 1.0M Oct 30 16:16 test.txt
$ stat test.txt
File: test.txt
Size: 1048576 Blocks: 8 IO Block: 4096 regular file
Device: 805h/2053d Inode: 35259462 Links: 1
Access: (0664/-rw-rw-r--) Uid: ( 1000/ yunh) Gid: ( 1000/ yunh)
Access: 2021-10-30 16:15:00.767760242 +0800
Modify: 2021-10-30 16:16:02.914508936 +0800
Change: 2021-10-30 16:16:02.914508936 +0800
Birth: -
$ du -sh test.txt
4.0K test.txt
上面的例子中,标称 1MB 的 test.txt 文件只占用 4KB 空间。
