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

时间:2016-07-16 07:17:13

标签: c linux file-io posix unbuffered

以下是我的问题描述:

我想在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未正确设置。

4 个答案:

答案 0 :(得分:14)

由于多种原因,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将在两次调用中被静默截断。

答案 1 :(得分:7)

如果它返回-1,你应该只对读取保释。从手册页:

  

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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