6GB 大文件的 read() 在 x86_64 上失败

read() of big 6GB file fails on x86_64

我的问题描述如下:

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

#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没有设置正确

只有 returns -1 时,您才应该放弃阅读。来自手册页:

On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested;

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

read 系统调用可以 return 一个小于请求大小的数字,原因有多种,正的非零 return 值不是错误,errno在这种情况下没有设置,它的值是不确定的。您应该继续循环阅读,直到 read returns 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 将在两次调用中被静默截断。

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

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

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