无法写入(2)大于2GB的文件(最高2TB)

时间:2017-07-06 04:30:37

标签: c

我有一个由C编写的程序。它计算一些东西并将输出写入文件。我的问题是它写的不超过2GB。我来一个简化的代码。

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <malloc.h>
#include <errno.h>
int main() {
    size_t size = 3221225472LL;
    char *map = malloc(size);
    size_t allocated = malloc_usable_size(map);
    int fd = open("myfile", O_RDWR|O_CREAT|O_TRUNC, (mode_t)0644);
    ssize_t written = write(fd, map, size);
    return 0;
}

虽然正在创建输出文件“myfile”,但对于我请求的大小超过2GB的大小,大小总是2GB(2147479552字节)。malloc()成功分配了所请求大小的内存(在这种情况下,“已分配” “是3GB)。 write()之后的errno为0。

环境如下

  • 操作系统:Ubuntu 16.04.1 x86_64
  • 物理内存大小:32GB
  • 文件系统:ext4

汇编:

gcc code.c -D_FILE_OFFSET_BITS=64 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE

这可能是什么原因?

增加: 得到两个回复后,我添加了重试代码如下:

int main() {
    size_t size = 3221225472LL;
    char *map = malloc(size);
    size_t allocated = malloc_usable_size(map);
    int fd = open("myfile", O_RDWR|O_CREAT|O_TRUNC, (mode_t)0644);
    ssize_t written = write(fd, map, size);
    while (written < size) {
        written += write(fd, &map[written], size-written);
    }
    return 0;
}

2 个答案:

答案 0 :(得分:13)

根据man page(强调我的)

  

在Linux上,write()(以及类似的系统调用)将传输 最多0x7ffff000(2,147,479,552)个字节 ,返回实际传输的字节数。 (在32位和64位系统上都是如此。)

答案 1 :(得分:4)

某些文件系统对文件大小有很大的限制,特别是FAT32。 disk quotas和资源限制(请参阅setrlimit(2)RLIMIT_FSIZE)都会限制文件大小(当然还有文件系统本身的可用空间)。

顺便说一下,你的written很可能(在你致电write(2)之后......)只有2 31 。你应该检查一下。

write(2)文件:

  

在Linux上,write()(和类似的系统调用)最多会传输          0x7ffff000(2,147,479,552)个字节,返回字节数          实际转移。 (32位和64位都是如此          系统。)

当然,对write(2)的给定调用永远不应该写入所有必需的字节(这在所有POSIX系统上都是如此,并且在20世纪80年代的Unix系统上也是如此)。例如,write到某些pipe(7)肯定无法写入那么多字节。

BTW,对write(2)的大量单次调用可能(或者至少可能)比使用较小缓冲区的几个调用效率低。最佳缓冲区大小是特定于实现的(并且与page cache和硬件相关)但可能是几十千字节,或者最多为几兆字节。

您可能更愿意使用<stdio.h> 缓冲 fwrite(3),但您应该检查返回的计数。

最后,您可以考虑在案例中使用mmap(2)。另请参阅msync(2)

请注意,对于大型文件,真正的瓶颈是硬件(磁盘本身)。因此,使用缓冲的fwrite来提高性能并不重要。

(你在评论中提到了一个太字节文件)

BTW,对于大TB级数据集,使用一些更高级别的方法(特别是数据库,可能带有sqlite,或者索引文件àlaGDBM)实际上可能更有效,因为你是然后只能编写数据的部分(或因为RDBMS在远程数据库服务器上运行,例如使用MariaDBPostGreSQL)。因人而异。但硬件带宽小于千兆字节/秒,因此写入1兆字节可能需要几个小时。即使使用巨大的交换大小,在32 GB的计算机上,如果没有thrashing,您将无法malloc太字节。

您也可以巧妙地使用posix_fadvise(2)来略微提高性能(但并不多:对于TB级文件而言,瓶颈是硬件)