Read() большого файла с 6 Гбайт не работает на x86_64
Вот описание моей проблемы:
Я хочу прочитать большой файл размером около 6.3 ГБ, все в памяти, используя системный вызов read
в C, но возникает ошибка.
Вот код:
#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
неправильно установлен.
Ответы
Ответ 1
Системный вызов 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
будет молча усечен в обоих вызовах.
Ответ 2
Вы должны освобождать только за чтение, если оно возвращает -1. На странице руководства:
При успешном завершении возвращается количество прочитанных байтов (ноль указывает конец файла), а файл позиция продвигается по этому номеру. Это не ошибка, если это число меньше, чем количество запрошенных байтов;
Я предполагаю, что на границе 2G в вашей файловой системе read()
может читать короткий буфер.
Ответ 3
Попробуйте #define _FILE_OFFSET_BITS 64 для open и #define _LARGEFILE64_SOURCE для lseek64. то вы можете читать файл записи размером более 2 ГБ
Ответ 4
Системный вызов read() не сможет читать огромные данные в однократном. Это зависит от многих факторов, таких как внутренний буфер ядра, реализация драйвера устройства на носителе. В вашем примере вы пытаетесь проверить, прочитало ли read() данные о размере длины, а затем распечатывает неудачу. Вам нужно продолжать чтение данных до тех пор, пока прочитанные байты не будут 0, также вам нужно проверить код возврата, возвращаемый read(), если он равен -1, то это означает, что есть некоторая ошибка при чтении, и в этом случае вам нужно установите errno.
Также я бы предложил не выделять огромную память за один снимок, даже если система могла бы выделять огромную память, так как это не была хорошая реализация. По возможности подумайте о том, чтобы разбить размер на несколько патронов.