我正在讨论在posix_fallocate和之间使用哪个功能fallocate.
posix_fallocate立即写入文件(将字符初始化为NULL).但是,fallocate不会更改文件大小(使用FALLOC_FL_KEEP_SIZE标志时).根据我的实验,似乎fallocate不会向文件写入NULL或零字符.
有人可以根据您的经验发表评论吗?谢谢你的时间.
拥有比显示长度更多的存储空间的文件是不常见的,所以除非你有充分的理由这样做(例如,你想使用文件长度来跟踪下载的程度,以便恢复它),最好使用默认的fallocate(2)行为.(没有FALLOC_FL_KEEP_SIZE).这与posix_fallocate(3)的语义相同.
fallocate(2)的手册页甚至说它的默认行为(没有标志)是实现posix_fallocate(3)的最佳方式,并指出它是一种可移植的分配空间的方式.
原始问题说明了将零写入文件.除了元数据,这些调用都不会写任何内容 如果你从已预先分配但尚未写入的空间中读取,你将得到零(不是之前磁盘空间中的任何内容,这将是一个很大的安全漏洞).您只能读取文件的末尾(长度,由fallocate,ftruncate或其他各种方式设置),因此如果您有一个零长度文件并使用FALLOC_FL_KEEP_SIZE进行分支,那么您将无法读取任何内容.与预分配无关,只与文件大小语义有关.
因此,如果您对POSIX语义很好,请使用它,因为它更便携.每个GNU/Linux系统都支持posix_fallocate(3),但其他一些系统也是如此.
但是,由于POSIX语义,它并不那么简单.如果在不支持预分配的文件系统上使用它,它仍然会成功,但是通过回退到在文件的每个块中实际写入零来实现.
测试程序:
#include <fcntl.h>
int main() {
int fd = open("foo", O_RDWR|O_CREAT, 0666);
if (fd < 0) return 1;
return posix_fallocate(fd, 0, 400000);
}
Run Code Online (Sandbox Code Playgroud)
在XFS上
$ strace ~/src/c/falloc
...
open("foo", O_RDWR|O_CREAT, 0666) = 3
fallocate(3, 0, 0, 400000) = 0
exit_group(0) = ?
Run Code Online (Sandbox Code Playgroud)
在fat32闪存驱动器上:
open("foo", O_RDWR|O_CREAT, 0666) = 3
fallocate(3, 0, 0, 400000) = -1 EOPNOTSUPP (Operation not supported)
fstat(3, {st_mode=S_IFREG|0755, st_size=400000, ...}) = 0
fstatfs(3, {f_type="MSDOS_SUPER_MAGIC", f_bsize=65536, f_blocks=122113, f_bfree=38274, f_bavail=38274, f_files=0, f_ffree=0, f_fsid={2145, 0}, f_namelen=1530, f_frsize=65536}) = 0
pread(3, "\0", 1, 6783) = 1
pwrite(3, "\0", 1, 6783) = 1
pread(3, "\0", 1, 72319) = 1
pwrite(3, "\0", 1, 72319) = 1
pread(3, "\0", 1, 137855) = 1
pwrite(3, "\0", 1, 137855) = 1
pread(3, "\0", 1, 203391) = 1
pwrite(3, "\0", 1, 203391) = 1
pread(3, "\0", 1, 268927) = 1
pwrite(3, "\0", 1, 268927) = 1
pread(3, "\0", 1, 334463) = 1
pwrite(3, "\0", 1, 334463) = 1
pread(3, "\0", 1, 399999) = 1
pwrite(3, "\0", 1, 399999) = 1
exit_group(0) = ?
Run Code Online (Sandbox Code Playgroud)
如果文件还不长,它确实避免了读取,但写入每个块仍然很可怕.
如果你想要一些简单的东西,我还是会选择posix_fallocate.它有一个FreeBSD手册页,由POSIX指定,因此每个符合POSIX标准的系统都提供它.一个缺点是,对于不支持预分配的文件系统,glibc会很糟糕.例如,请参阅https://plus.google.com/+AaronSeigo/posts/FGtXM13QuhQ.对于使用大文件的程序(例如种子),这可能非常糟糕.
您可以感谢POSIX语义要求glibc执行此操作,因为它没有为"文件系统不支持预分配"定义错误代码. http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html.它还保证如果调用成功,后续写入分配的区域将不会因磁盘空间不足而失败.所以posix设计没有提供一种方法来处理调用者关心效率/性能/碎片而不是磁盘空间保证的情况.这会强制POSIX实现执行读写循环,而不是将其作为需要磁盘空间保证的调用者的选项.谢谢POSIX ......
我不知道当文件系统不支持预分配时,posix_fallocate的非GNU实现是否同样会回到极慢的读写行为.(FreeBSD,Solaris?).显然OS X(Darwin)没有实现posix_fallocate,除非它是最近的.
如果您希望在许多平台上支持预分配,但如果操作系统只能尝试预分配,则不必再回读 - 然后写入,您必须使用任何特定于平台的方法.例如,请查看 https://github.com/arvidn/libtorrent/blob/master/src/file.cpp
搜索file :: set_size.它有几个ifdeffed块,具体取决于编译目标支持的内容,从windows代码开始加载DLL并在那里执行,然后fcntl F_PREALLOCATE,或fcntl F_ALLOCSP64,然后是Linux fallocate(2),然后回退到使用posix_fallocate.此外,还发现了OS X Darwin的2007年帖子列表:http://lists.apple.com/archives/darwin-dev/2007/Dec/msg00040.html
至少有一点信息来自 Fallocate(2) 手册页:
int fallocate(int fd, int mode, off_t offset, off_t len);
DESCRIPTION
This is a nonportable, Linux-specific system call.
Run Code Online (Sandbox Code Playgroud)
尽管系统调用文档没有说明,但 Fallocate(1) 程序手册页显示:
As of the Linux Kernel v2.6.31, the fallocate system call is supported
by the btrfs, ext4, ocfs2, and xfs filesystems.
Run Code Online (Sandbox Code Playgroud)
这对我来说很有意义,因为 NTFS、FAT、CDFS 和大多数其他常见文件系统在磁盘上没有支持调用的内部机制。我认为对这些的支持将由内核缓冲,并且该设置不会在系统启动时持续存在。