【问题标题】:read() of big 6GB file fails on x86_646GB 大文件的 read() 在 x86_64 上失败
【发布时间】:2016-07-16 07:17:12
【问题描述】:

这是我的问题的描述:

我想使用 C 中的read 系统调用读取一个大约 6.3GB 的大文件到内存中,但出现错误。 代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>

int main(int argc, char* argv[]) {
    int _fd = open(argv[1], O_RDONLY, (mode_t) 0400);
    if (_fd == -1)
        return 1;
    off_t size = lseek(_fd, 0, SEEK_END);
    printf("total size: %lld\n", size);
    lseek(_fd, 0, SEEK_SET);
    char *buffer = malloc(size);
    assert(buffer);
    off_t total = 0;
    ssize_t ret = read(_fd, buffer, size);
    if (ret != size) {
        printf("read fail, %lld, reason:%s\n", ret, strerror(errno));
        printf("int max: %d\n", INT_MAX);
    }
}

然后编译:

gcc read_test.c

然后运行:

./a.out bigfile

输出:

total size: 6685526352
read fail, 2147479552, reason:Success
int max: 2147483647

系统环境是

 3.10.0_1-0-0-8 #1 SMP Thu Oct 29 13:04:32 CST 2015 x86_64 x86_64 x86_64 GNU/Linux

有两个地方我不明白:

  1. 读取大文件失败,但读取小文​​件失败。
  2. 即使有错误,似乎errno设置不正确。

【问题讨论】:

  • 很好,调用成功读取了2147479552字节。你需要循环直到你消耗了所有的data.btw?你总共有多少内存?
  • 为什么?在少数情况下,您确实需要内存中的整个文件。
  • 您的系统是否限制了可用内存?你试过ulimit -s unlimited 吗?
  • 如果你使用open()read()等POSIX函数,你也可以使用POSIX stat()和/或fstat()直接获取文件的大小。
  • 根据您将这个巨大的文件存储在内存中后要如何处理它,mmap 可能比read 更合适。

标签: c linux file-io posix unbuffered


【解决方案1】:

read 系统调用可能由于多种原因返回小于请求大小的数字,正的非零返回值不是错误,errno 在这种情况下未设置,其值是不确定的。您应该继续循环阅读,直到 read 返回 0 以表示文件结尾或 -1 表示错误。依靠read 在单个调用中读取完整块是一个非常常见的错误,即使是从常规文件中也是如此。使用fread 以获得更简单的语义。

您打印INT_MAX 的值,这与您的问题无关。 off_tsize_t 的大小是有趣的。在您的平台上,64 位 GNU/Linux,您很幸运 off_tsize_t 都是 64 位长。根据定义,ssize_tsize_t 的大小相同。在其他 64 位平台上,off_t 可能小于size_t,从而妨碍正确评估文件大小,或者size_t 可能小于off_t,让malloc 分配一个小于文件大小的块。请注意,在这种情况下,read 将传递相同的较小大小,因为 size 在两个调用中都会被静默截断。

【讨论】:

  • 非常感谢!当我继续循环阅读直到达到 0 或 -1 时,一切正常。
  • @zhanglistar:这个问题没有明确的答案:对于小块,fread 可能更快,因为在标准 I/O 包中默认执行缓冲;对于大块,它取决于实际的实现。请注意,fread 是便携式解决方案。 read() 是 Posix 中标准化的系统调用,并非在所有系统上都可用。
【解决方案2】:

只有当它返回 -1 时,你才应该放弃读取。来自手册页:

成功时,返回读取的字节数(零表示结束 文件)和文件 位置由这个数字提前。如果此数字小于 请求的字节数;

我的猜测是,在文件系统的 2G 边界处,read() 可以读取短缓冲区。

【讨论】:

    【解决方案3】:

    尝试使用#define _FILE_OFFSET_BITS 64 打开,使用#define _LARGEFILE64_SOURCE 使用lseek64。那么你可以读写大于2GB的文件

    【讨论】:

      【解决方案4】:

      read() 系统调用将无法一次性读取大量数据。这取决于许多因素,如内核的内部缓冲区、媒体的设备驱动程序实现。在您的示例中,您正在尝试检查 read() 是否已读取长度大小的数据,如果没有则打印失败。需要一直读取数据,直到读取的字节数为0,还需要检查read()返回的返回码,如果是-1,则表示读取失败,此时需要检查设置的 errno。

      此外,我建议不要一次性分配大量内存,即使系统能够分配大量内存,因为它不是一个好的实现。如果可能,请考虑将尺寸缩小到一些夹头。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-15
        • 2012-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-12
        • 1970-01-01
        • 2011-05-04
        相关资源
        最近更新 更多