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.

Также я бы предложил не выделять огромную память за один снимок, даже если система могла бы выделять огромную память, так как это не была хорошая реализация. По возможности подумайте о том, чтобы разбить размер на несколько патронов.