Linux环境下如何用C语言快速创建无空洞大文件?
嘿,这个问题我太有共鸣了!当年fallocate秒建8GB大文件的速度确实惊艳,我来给你捋清楚这里面的门道~
你回忆里的“无空洞文件”,其实是内核直接预分配磁盘块并初始化为零的文件——这类文件用ls -lh和du -h看大小完全一致,没有“磁盘占用远小于文件标称大小”的空洞情况,而且因为是内核级操作,速度快到离谱。
现在你觉得创建的文件有洞,大概率是混淆了“预分配零块”和“空洞文件”:空洞文件是通过lseek跳着写出来的,磁盘上根本没有对应中间区域的块;而fallocate预分配的文件,所有块都实实在在占了磁盘空间,只是内容是零,本质上不是空洞。
方式1:直接调用Linux专属的fallocate()系统调用(最快)
这个就是当年fallocate工具底层用的方法,从Linux 2.6.23内核开始就存在,至今完全可用。只要调用时不指定FALLOC_FL_KEEP_SIZE标志,内核就会自动扩展文件大小、预分配所有需要的磁盘块并填零,全程在内核态完成,速度拉满。
示例代码:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { const char* filename = "fast_bigfile"; // 创建/截断文件为读写模式 int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("Failed to open file"); return 1; } off_t target_size = 8LL * 1024 * 1024 * 1024; // 8GB // 0表示无额外标志,等价于FALLOC_FL_FALLOCATE int ret = fallocate(fd, 0, 0, target_size); if (ret == -1) { perror("Failed to allocate file space"); close(fd); return 1; } close(fd); printf("8GB file created successfully (no holes, kernel-level zeroed)\n"); return 0; }
方式2:用POSIX标准的posix_fallocate()(更便携)
如果需要兼顾其他类Unix系统,可以用这个POSIX标准函数。在Linux上它其实还是底层调用fallocate()实现,效果和速度跟上面差不多;但在BSD等系统上,可能会用用户态写零的方式,速度会慢一些。
示例代码:
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> int main() { const char* filename = "portable_bigfile"; int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { perror("Failed to open file"); return 1; } off_t target_size = 8LL * 1024 * 1024 * 1024; // 8GB int ret = posix_fallocate(fd, 0, target_size); if (ret != 0) { errno = ret; perror("Failed to allocate file space (POSIX)"); close(fd); return 1; } close(fd); printf("8GB file created successfully (POSIX compliant, no holes)\n"); return 0; }
fallocate()系统调用确实是Linux专属的,而且从诞生至今没有被废弃——只是后来新增了FALLOC_FL_KEEP_SIZE等标志,让工具更灵活。比如现在如果用fallocate -k -l 8G file,就会只预分配空间但不改变文件大小,这时候读超过原文件大小的部分会返回EOF,容易被误以为是空洞,但其实磁盘空间已经被占了。
当年你用的fallocate应该是默认调用了无额外标志的fallocate(),也就是直接扩展文件大小并预分配填零,这个逻辑现在依然可以通过代码实现,完全没问题。
创建完文件后,可以用这两个命令确认:
ls -lh filename和du -h filename:如果两个输出的大小一致,说明没有空洞(磁盘确实占了对应空间)filefrag -v filename:查看文件的磁盘块分布,如果没有“hole”标记,就是真正的无空洞文件
内容的提问来源于stack exchange,提问作者Nick

