大的6GB文件的read()在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没有正确设置。

由于多种原因, 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,你应该只保留读取。 从手册页:

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

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

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

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

另外,我建议不要一次性分配大量的内存,即使系统能够分配巨大的内存,因为它不是一个好的实现。 如果可能的话,考虑把尺寸打破一些卡盘。