空洞文件(hole file)的描述:
使用
lseek
可以修改文件的当前读写位置偏移量,此函数不但可以改变位置偏移量,并且还允许文件偏移量超出文件长度,这是什么意思呢?譬如有一个 test_file
,该文件的大小是
4K
(也就是
4096
个字节),可以通过
lseek
系统调用将该文件的读写偏移量移动到偏移文件头部 6000 个字节处。
接下来使用
write()
函数对文件进行写入操作,也就是说此时将是从偏移文件头部
6000
个字节处开始写 入数据,也就意味着 4096~6000 字节之间出现了一个空洞,因为这部分空间并没有写入任何数据,所以形成了空洞,这部分区域就被称为文件空洞,那么相应的该文件也被称为空洞文件。
文件空洞部分实际上并不会占用任何物理空间,直到在某个时刻对空洞部分进行写入数据时才会为它分配对应的空间,但是空洞文件形成时,逻辑上该文件的大小是包含了空洞部分的大小的,这点需要注意。
空洞文件(hole file)的作用:
空洞文件对多线程共同操作文件是及其有用的,有时候我们创建 一个很大的文件,如果单个线程从头开始依次构建该文件需要很长的时间,有一种思路就是将文件分为多段,然后使用多线程来操作,每个线程负责其中一段数据的写入。
这个有点像我们现实生活当中施工队修路 的感觉,比如说修建一条高速公路,单个施工队修筑会很慢,这个时候可以安排多个施工队,每一个施工队负责修建其中一段,最后将他们连接起来。
空洞文件(hole file)的实际应用场景:
⚫ 在使用迅雷下载文件时,还未下载完成,就发现该文件已经占据了全部文件大小的空间,这也是空洞文件;下载时如果没有空洞文件,多线程下载时文件就只能从一个地方写入,这就不能发挥多线程的作用了;如果有了空洞文件,可以从不同的地址同时写入,就达到了多线程的优势;
⚫
在创建虚拟机时,你给虚拟机分配了
100G
的磁盘空间,但其实系统安装完成之后,开始也不过只用了 3
、
4G
的磁盘空间,如果一开始就把
100G
分配出去,资源是很大的浪费。
示例代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
int fd;
int ret;
char buffer[1024];
int i;
/* 打开文件 */
fd = open("./hole_file", O_WRONLY | O_CREAT | O_EXCL,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (-1 == fd)
{
perror("open error");
exit(-1);
}
/* 将文件读写位置移动到偏移文件头 4096 个字节(4K)处 */
ret = lseek(fd, 4096, SEEK_SET);
if (-1 == ret)
{
perror("lseek error");
goto err;
}
/* 初始化 buffer 为 0xFF */
memset(buffer, 0xFF, sizeof(buffer));
/* 循环写入 4 次,每次写入 1K */
for (i = 0; i < 4; i++)
{
ret = write(fd, buffer, sizeof(buffer));
if (-1 == ret)
{
perror("write error");
goto err;
}
}
ret = 0;
err:
/* 关闭文件 */
close(fd);
exit(ret);
}
示例代码中,我们使用
open
函数新建了一个文件
hole_file
,在
Linux
系统中,新建文件大小是
0
,也就 是没有任何数据写入,此时使用lseek
函数将读写偏移量移动到
4K
字节处,再使用
write
函数写入数据
0xFF
, 每次写入 1K
,一共写入
4
次,也就是写入了
4K
数据,也就意味着该文件前
4K
是文件空洞部分,而后
4K 数据才是真正写入的数据。
编译测试:
使用 ls 命令查看到空洞文件的大小是 8K,使用 ls 命令查看到的大小是文件的逻辑大小,自然是包括了空洞部分大小和真实数据部分大小;当使用 du 命令查看空洞文件时,其大小显示为 4K,du 命令查看到的大小是文件实际占用存储块的大小。