概念
什么是空洞文件(hole file)?💁♂️在Linux中,lseek的系统调用是可以改变在文件上面的偏移量的,而且还允许其超出文件的长度。偏移量一旦超出了文件的长度,下一次进行文件IO写入操作文件的时候便会延续偏移量的位置继续写入,进而在文件中间产生了空洞的部分,这部分会以”\0”填充,而从原来的文件结尾到新写入数据间的这段空间就被称为“文件空洞”。
在Linux中,EOF(文件结束符)并不是一个字符,而是在读取到文件末尾的时候返回的一个信号值,也就是-1。
文件空洞部分实际上是不会占用任何的物理空间的,直到在某个时刻对空洞部分进行写入文件内容的时候才会为它分配对应的空间。但是在空洞文件形成的时候,逻辑上面的文件大小是分配了空洞部分的大小的。
👯♀️👯♂️👯♀️👯♂️👯♀️👯♂️👯♀️👯♂️
实验
接下来可以通过一个实验来验证空洞文件的形成,并且使用cat和cp两种方式对空洞文件进行操作,看看他们对应的不同效果。
首先使用dd命令产生一个空洞文件,具体可以查看dd的使用方法。
- if - 输入文件
- of - 输出文件
- seek - 设置输出文件的偏移量
- skip - 设置输入文件的偏移量
- bs - 是ibs和obs的合集
接下来从/dev/urandom
中读取内容,写入到hole.file
文件中,在写入之前,对hole.file
文件设置了999的偏移量,bs设置了4096的大小,同时设置写入的block数量为1,这样将会产生一个逻辑长度为1000数据块的文件。
~$ dd if=/dev/urandom of=hole.file bs=4096 seek=999 count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.000449329 s, 9.1 MB/s
产生之后,使用ls命令查看文件大小是4.0MB
~$ ls -lh hole.file
-rw-r--r-- 1 chenzhiling01 root 4.0M 8月 21 15:12 hole.file
使用du命令查看文件大小是4.0KB
~$ du -h hole.file
4.0K hole.file
相差如此悬殊!!🧐
使用od命令来查看hole.file
文件的二进制内容
~$ od -c hole.file
0000000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
...
会看到其实整个hole.file
文件的开始是用许多的”\0”来填充的,由于上面设置了偏移量比较大,可以设置小一点的seek来观察。
接下来使用cat来重定向文件到一个新的文件
~$ cat hole.file > hole.cat
再使用cp命令将文件拷贝一份出来
~$ cp hole.file hole.cp
使用ls命令查看两个新生文件的大小
~$ ls -lh hole.cat hole.cp
-rw-r--r-- 1 chenzhiling01 root 4.0M 8月 21 15:39 hole.cat
-rw-r--r-- 1 chenzhiling01 root 4.0M 8月 21 15:40 hole.cp
再使用du查看两个新生文件的大小
~$ du -h hole.cat hole.cp
4.0M hole.cat
4.0K hole.cp
会看到使用cat重定向得到的文件的大小使用ls和du都是4.0MB,而使用cp命令得到的文件大小信息和原来的文件是一样的。
总结
为什么ls和du命令得到的文件大小会相差如此巨大呢?是因为ls获取得到的是文件的逻辑大小,而du获取得到的是文件的实际占用物理块的大小。也就是说,当产生空洞文件的时候,文件系统并不会将空洞文件部分对应也分配好空间,那样是相当浪费的,而且还会被一些黑客利用,在系统中产生大量的空洞文件来耗尽系统的存储资源。
🦁一个小插曲,我发现ls -sh列出来的大小和du出来的大小是一样的,所以可以认为ls -s所获取得到的是文件实际占用的存储块的大小。
因此在文件系统上面观察,对应的文件看上去是和普通文件一样,该多大就多大,但是实际上并不会马上为文件分配对应大小的存储。
而我们使用cat和cp命令得到的文件大小竟然是不一样的,也就是说cp命令和重定向的过程是进行了不一样的操作的。
cat命令重定向内容到新的文件的时候,其遇到了空洞部分,会用0来填充,这样空洞部分其实也就有了内容了,对应需要为他分配物理存储block,这样文件的真是大小其实就是4MB,但是cp命令在遇到空洞部分的时候,会模拟源文件的空洞调用seek,进行偏移之后再进行内容拷贝,这样其实生成的新文件和源文件是一样的,空洞部分都是不被分配真是存储空间的。
作用
空洞文件看上去好像是一个不太靠谱不太安全的操作,但其实在很多情况都很有用:
- 像在我们平时使用迅雷下载的时候,刚开始下载但是本地的下载文件就已经好几百兆了,这就利用了空洞文件。为了能够并行下载,创建空洞文件可以让多线程在不同的seek上面开始写入文件,如果不是空洞文件就只能串行写入了。
- 在创建虚拟机的时候,我们会使用img工具生成一个例如50GB大小的镜像文件,但是其实在安装完系统之后,镜像的大小可能只有4GB,也就是说img并不会马上就占用掉物理存储空间的50GB,而是在未来使用过程中不断增加的。
🤓🤓🤓