10

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

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

4

3 回答 3

6

文件占用的存储空间超过其显示长度并不常见,因此除非您有充分的理由这样做(例如,您想使用文件长度来跟踪下载的距离,以便恢复它),最好使用默认的 fallocate(2) 行为。(没有 FALLOC_FL_KEEP_SIZE)。这与 posix_fallocate(3) 的语义相同。

fallocate(2) 的手册页甚至说它的默认行为(无标志)旨在作为实现 posix_fallocate(3) 的最佳方式,并指出这是一种分配空间的可移植方式。

最初的问题是关于将零写入文件。除了元数据之外,这些调用都不会写入任何内容。如果您从已预先分配但尚未写入的空间中读取,您将得到零(不是该磁盘空间之前的任何内容,这将是一个很大的安全漏洞)。您只能读取到文件的末尾(长度,由 fallocate、ftruncate 或各种其他方式设置),因此如果您有一个零长度文件并使用 FALLOC_FL_KEEP_SIZE 进行 fallocate,那么您将无法读取任何内容。与预分配无关,只是文件大小语义。

因此,如果您对 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);
}

在 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

搜索文件::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

于 2014-05-07T04:24:53.477 回答
4

我认为您没有查看说

   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() 的手册页似乎没有提到相同的内容,但相反,查看源代码here,它似乎写入了文件的每个块(第 88 行)。

人 fallocate 人 posix_fallocate

于 2012-12-28T00:40:16.823 回答
2

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

于 2012-12-28T00:31:30.553 回答