fallocate vs posix_fallocate

Bil*_*ill 9 c c++ posix

我正在讨论在posix_fallocate和之间使用哪个功能fallocate. posix_fallocate立即写入文件(将字符初始化为NULL).但是,fallocate不会更改文件大小(使用FALLOC_FL_KEEP_SIZE标志时).根据我的实验,似乎fallocate不会向文件写入NULL或零字符.

有人可以根据您的经验发表评论吗?谢谢你的时间.

Pet*_*des 5

拥有比显示长度更多的存储空间的文件是不常见的,所以除非你有充分的理由这样做(例如,你想使用文件长度来跟踪下载的程度,以便恢复它),最好使用默认的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


wal*_*lyk 2

至少有一点信息来自 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 和大多数其他常见文件系统在磁盘上没有支持调用的内部机制。我认为对这些的支持将由内核缓冲,并且该设置不会在系统启动时持续存在。