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

zha*_*tar 14 c linux file-io posix unbuffered

这是我的问题的描述:

我想读取一个大的文件,大约6.3GB,所有内存使用readC中的系统调用,但发生错误.这是代码:

#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);
    }
}
Run Code Online (Sandbox Code Playgroud)

并编译它:

gcc read_test.c
Run Code Online (Sandbox Code Playgroud)

然后运行:

./a.out bigfile
Run Code Online (Sandbox Code Playgroud)

输出:

total size: 6685526352
read fail, 2147479552, reason:Success
int max: 2147483647
Run Code Online (Sandbox Code Playgroud)

系统环境是

 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
Run Code Online (Sandbox Code Playgroud)

有两个地方我不明白:

  1. 读取在大文件上失败,但在小文件上失败.
  2. 即使出现错误,似乎errno也没有正确设置.

chq*_*lie 14

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

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

  • @zhanglistar:这个问题没有明确答案:对于小块,由于标准I/O包中默认执行缓冲,`fread`可能会更快; 对于大块,它取决于实际的实现.请注意,`fread`是便携式解决方案.`read()`是在Posix中标准化的系统调用,并非在所有系统上都可用. (3认同)

eva*_*itl 7

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

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

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