这是我的问题的描述:
我想读取一个大的文件,大约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
有两个地方我不明白:
errno
没有正确设置。 由于多种原因, read
系统调用可以返回比请求大小更小的数字,正的非零返回值不是错误,在这种情况下, errno
不会被设置,其值是不确定的。 您应该继续读循环直到read
返回0
为文件结尾或-1
为一个错误。 这是一个非常常见的错误,依靠read
来读一个完整的块在一个单一的调用,即使是从普通的文件。 使用fread
更简单的语义。
您打印与您的问题无关的INT_MAX
的值。 off_t
和size_t
的大小是有趣的。 在你的64位GNU / Linux平台上,你很幸运, off_t
和size_t
都是64位的。 根据定义, ssize_t
与size_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。
另外,我建议不要一次性分配大量的内存,即使系统能够分配巨大的内存,因为它不是一个好的实现。 如果可能的话,考虑把尺寸打破一些卡盘。