C语言 无法写入(2)个大于2GB(最多2TB)的文件

dgsult0t  于 2023-06-28  发布在  其他
关注(0)|答案(2)|浏览(135)

我有一个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。
环境如下

  • OS: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;
}
vh0rcniy

vh0rcniy1#

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

cgh8pdjw

cgh8pdjw2#

一些文件系统对文件大小有很强的限制,特别是FAT32。disk quotas和资源限制(参见RLIMIT_FSIZEsetrlimit(2))都限制了文件大小(当然还有文件系统本身的可用空间)。
顺便说一句,你的written很可能(在你调用write(2)之后...)只有231。你应该检查一下。
write(2)文档:
在Linux上,write()(和类似的系统调用)最多传输0x 7 ffff 000(2,147,479,552)字节,返回实际传输的字节数。(在32位和64位系统上都是如此。)
当然,对write(2)的给定调用永远不应该被期望已经写入了所有需要的字节(在所有POSIX系统上都是如此,在20世纪80年代的Unix系统上也是如此)。例如,一个write到某个pipe(7)肯定无法写入那么多字节。
顺便说一句,对write(2)的一个巨大的单次调用可能(或者至少可能)比使用较小缓冲区的几个调用效率更低。最佳缓冲区大小是特定于实现的(也与page cache和硬件有关),但可能是几十KB,或最多1 MB。
您可能更喜欢使用<stdio.h>bufferedfwrite(3),但您应该检查返回的计数。
最后,您可以考虑在您的案例中使用mmap(2)。参见msync(2)
请注意,对于大型文件,真实的的瓶颈是硬件(磁盘本身)。因此,使用缓冲的fwrite来提高性能并不重要。
(you在注解中提到TB文件)
顺便说一句,对于大的TB数据集,使用一些更高级别的方法(特别是数据库,可能是sqlite,或者索引文件à la GDBM)实际上可能更有效,因为这样你就只能写数据的一部分(或者因为RDBMS运行在远程数据库服务器上,例如,RDBMS)。使用MariaDBPostGreSQL)。但是硬件带宽小于千兆字节/秒,所以写入一个兆字节可能需要几个小时。即使交换容量很大,如果没有thrashing,在32 GB的机器上也无法传输malloc一TB。
您还可以巧妙地使用posix_fadvise(2)来略微提高性能(但不是很多:对于TB级文件,瓶颈是硬件)

相关问题