目录
4.3 同一个进程中通过 dup(dup2)函数对文件描述符进行复制
1 空洞文件
1.1 空洞文件概念
空洞文件(Sparse File)是一种节省磁盘空间的文件类型,它允许文件包含未分配的空间,这些空间在文件系统中被视为“空洞”。这意味着文件的逻辑大小可能很大,但实际占用的磁盘空间却很小,因为只有实际写入数据的部分才会占用空间。这种特性使得空洞文件非常适合用于大型日志文件、数据库文件或备份文件等场景,其中文件的大部分可能从未被写入。
空洞文件的工作原理是,文件系统会在文件中创建一个虚拟的空洞区域,当尝试读取这些空洞区域时,操作系统会返回默认值(通常是零),而不是实际从磁盘读取数据。这可以减少磁盘I/O操作,提高访问速度,并且因为未分配的空洞不占用实际的磁盘空间,所以可以节省大量的存储资源。在实际使用中,当应用程序写入数据到空洞文件的空洞区域时,文件系统会分配实际的磁盘块来存储这些数据,空洞随即被填充。因此,空洞文件的大小可能会随着数据的写入而动态变化。
1.2 空洞文件示例
下面代码演示了如何在Linux系统中创建一个空洞文件,并向其中写入数据。代码如下:
#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);
}
通过这个程序,最终会在文件./hole_file
中创建一个4KB大小的空洞,然后向其中写入4KB的数据。由于使用了lseek
跳过了文件开始的4KB,所以在文件的开始处会有一个4KB的空洞区域,后面跟着连续的4KB数据。运行并查看文件大小:
使用 ls 命令查看到空洞文件的大小是 8K,使用 ls 命令查看到的大小包括了空洞部分大小和真实数据部分大小;当使用 du 命令查看空洞文件时,其大小显示为 4K, du 命令查看到的大小是文件实际占用存储块的大小。
2 多次打开同一个文件
在Linux系统中,可以多次打开同一个文件,每次打开文件都会获得一个新的文件描述符(file descriptor)。文件描述符是一个非负整数,用于标识特定的文件或I/O通道。以下是一些关于多次打开同一个文件的要点:
-
独立的文件描述符:每次调用
open
函数时,如果成功,都会返回一个新的文件描述符,即使打开的是同一个文件。同理在关闭文件的时候也需要调用 close 依次关闭各个文件描述符。 -
独立的文件指针:每个文件描述符都有自己的文件指针,用于记录当前的读写位置。这意