fallocate vs posix_fallocate

我正在讨论在posix_fallocatefallocate之间使用哪个函数。 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  int main() { int fd = open("foo", O_RDWR|O_CREAT, 0666); if (fd < 0) return 1; return posix_fallocate(fd, 0, 400000); } 

在XFS上

 $ strace ~/src/c/falloc ... open("foo", O_RDWR|O_CREAT, 0666) = 3 fallocate(3, 0, 0, 400000) = 0 exit_group(0) = ? 

在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) = ? 

如果文件还不长,它确实可以避免读取,但写入每个块仍然很糟糕。

如果你想要一些简单的东西,我还是会选择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年post: http : //lists.apple.com/archives/darwin-dev/2007/Dec/msg00040.html

我认为你没看过那些说的文件

  The mode argument determines the operation to be performed on the given range. Currently only one flag is supported for mode: FALLOC_FL_KEEP_SIZE This flag allocates and initializes to zero the disk space within the range specified by offset and len. After a successful call, subsequent writes into this range are guaranteed not to fail because of lack of disk space. Preallocating zeroed blocks beyond the end of the file is useful for optimizing append workloads. Preallocating blocks does not change the file size (as reported by stat(2)) even if it is less than offset+len. If FALLOC_FL_KEEP_SIZE flag is not specified in mode, the default behavior is almost same as when this flag is specified. The only difference is that on success, the file size will be changed if offset + len is greater than the file size. This default behavior closely resembles the behavior of the posix_fallocate(3) library function, and is intended as a method of optimally implementing that function. 

posix_fallocate()的手册页似乎没有提到相同的东西,而是在这里看源,它似乎写了文件的每个块(第88行)。

man fallocate man posix_fallocate

至少有一点信息来自fallocate(2)手册页:

 int fallocate(int fd, int mode, off_t offset, off_t len); DESCRIPTION This is a nonportable, Linux-specific system call. 

虽然系统调用文档没有说明,但是fallocate(1)程序手册页说:

 As of the Linux Kernel v2.6.31, the fallocate system call is supported by the btrfs, ext4, ocfs2, and xfs filesystems. 

这对我来说很有意义,因为NTFS,FAT,CDFS和大多数其他常见文件系统在磁盘上没有内部机制来支持调用。 我认为对内核的支持将被内核缓冲,并且设置不会在系统引导之间持续存在。