首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >错误定位vs posix_fallocate

错误定位vs posix_fallocate
EN

Stack Overflow用户
提问于 2012-12-28 08:21:20
回答 3查看 10.6K关注 0票数 9

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

有没有人能根据你的经验发表意见?耽误您时间,实在对不起。

EN

回答 3

Stack Overflow用户

发布于 2014-05-07 12:24:53

文件占用比显示长度更多的存储空间是不常见的,所以除非你有很好的理由这样做(例如,你想使用文件长度来跟踪下载到多远,以便恢复它),最好使用默认的file (2)行为。(不使用FALLOC_FL_KEEP_SIZE)。这与posix_fallocate(3)的语义相同。

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

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

因此,如果您对POSIX语义没什么意见,那就使用它,因为它更易于移植。每个GNU/Linux系统都会支持posix_fallocate(3),但其他一些系统也会支持。

但是,多亏了POSIX语义,事情就没那么简单了。如果您在不支持预分配的文件系统上使用它,它仍然会成功,但要做到这一点,需要在文件的每个块中实际写入一个零。

测试程序:

代码语言:javascript
复制
#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上

代码语言:javascript
复制
$ strace ~/src/c/falloc
...
open("foo", O_RDWR|O_CREAT, 0666) = 3
fallocate(3, 0, 0, 400000)              = 0
exit_group(0)                           = ?

在fat32闪存驱动器上:

代码语言:javascript
复制
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,请参阅。对于处理大文件(例如torrents)的程序来说,这可能真的很糟糕。

您可以感谢POSIX语义要求glibc这样做,因为它没有为“文件系统不支持预分配”定义错误代码。http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html。它还保证,如果调用成功,后续写入已分配区域的操作不会因为磁盘空间不足而失败。因此,posix设计没有提供一种方法来处理调用者关心效率/性能/碎片而不是磁盘空间保证的情况。这迫使POSIX实现执行读写循环,而不是将其留给需要磁盘空间保证的调用者。谢谢POSIX。

我不知道当文件系统不支持预分配时,posix_fallocate的非GNU实现是否同样会退回到极慢的读写行为。(FreeBSD,Solaris?)显然,OS (达尔文)并没有实现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。此外,我还找到了这篇2007年发布在OS上的文章:http://lists.apple.com/archives/darwin-dev/2007/Dec/msg00040.html

票数 6
EN

Stack Overflow用户

发布于 2012-12-28 08:40:17

我猜你没有看文档上写着

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行)。

man fallocate man posix_fallocate

票数 4
EN

Stack Overflow用户

发布于 2012-12-28 08:31:31

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

代码语言:javascript
复制
int fallocate(int fd, int mode, off_t offset, off_t len);

DESCRIPTION
   This is a nonportable, Linux-specific system call.

尽管系统调用文档中没有说明,但fallocate(1)程序手册页上写道:

代码语言:javascript
复制
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和大多数其他常见文件系统在磁盘上没有支持调用的内部机制。我假设对这些的支持将由内核缓冲,并且该设置不会在系统引导时持续存在。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14063046

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档