15

这是我的问题的描述:

我想使用readC 中的系统调用读取一个大约 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

4 回答 4

15

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

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

于 2016-07-16T08:16:09.233 回答
7

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

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

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

于 2016-07-16T07:49:59.183 回答
0

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

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

于 2016-07-22T18:20:28.933 回答
0

尝试#define _FILE_OFFSET_BITS 64 打开,#define _LARGEFILE64_SOURCE 尝试 lseek64。然后您可以读取大于 2GB 的写入文件

于 2016-07-18T06:53:23.010 回答