Ошибка шины mmap при записи в файл MAP_SHARED более 2 ГБ

Я пытаюсь создать файл сопоставления памяти с помощью MAP_SHARED. Я сталкиваюсь с проблемами, когда размер файла достигает 2 ГБ. Код, вставленный ниже, является тем, что я использую (в качестве теста).

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

#define MY_FILE "/dev/shm/mmap_test"
#define BLOCK_SIZE (1024*1024)
#define NUM_FILES 1

void mk_file(int f_num)
{ 
    uint64_t len = 0;
    int fd, j, k;
    char tmp_file[1024], *x, *rr_addr;

    // Open file in /dev/shm
    sprintf(tmp_file, "%s%d", MY_FILE, f_num);
    fd = open(tmp_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

    if (fd == -1)
    {
        perror("file open");
        exit(-1);
    }

    // 16Gb file 
    len = 16UL * 1024 * 1024 * 1024;
    printf("len: %ld Gb\n", len/(1024*1024*1024));

    printf("Mapping %ld blocks\n", len/BLOCK_SIZE);

    for (j = 0; j < len/BLOCK_SIZE; j++) {
        // Increase the file size
        ftruncate(fd, ((j + 1) * BLOCK_SIZE));

        // Just mmap memory... don't have file backing
        //rr_addr = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0);

        // MMAP a region to the file at a given offset
        rr_addr = mmap(NULL, BLOCK_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_NORESERVE, fd, (j * BLOCK_SIZE));

        if (rr_addr == MAP_FAILED) {
            perror("mmap error");
            printf("INDEX: %d\n", j);
            exit(-1);
        }

        // Write to every byte of allocated memory
        x = (char *) rr_addr;

        for (k = 0; k < BLOCK_SIZE; k++)
        {
            *x = '1';
            x++;
        }
    }

    return;
}

int main(int argc, char **argv)
{
    uint64_t i;

    for (i = 0; i < NUM_FILES; i++)
       mk_file(i);

    return 0;
}

В приведенном выше коде я получаю ошибку шины, когда смещение в файле достигает 2 ГБ. Вот что я попробовал:

  1. Если я изменю NUM_FILES на 16, а len на 1GB, у меня не возникнет никаких проблем.
  2. Если я удаляю цикл for, который записывает в память (только mmap), программа не падает (даже для len, намного превышающего 2 ГБ), поскольку ядро ​​linux фактически не отображает страницы в файл, пока вы не выполните чтение / запись в область mmap.
  3. Если я изменю вызов mmap с MAP_SHARED на MAP_ANON (раскомментируем первый вызов mmap и закомментирую второй) и не буду связан с файлом, проблем не будет (даже запись завершится успешно).
  4. На /dev/shm (30 ГБ) достаточно места, здесь я использую только 16 ГБ.
  5. Мне не нужно писать на каждый выделенный байт. Мне нужно только записать в последнюю область mmap (переместить внутренний цикл for наружу), и если offset + BLOCK_SIZE >= 2gb, я получаю ошибку шины.
  6. Я пробовал это на Ubuntu 13.10 и CentOS 6.4, и у обоих одинаковая проблема.

Мне интересно, если это проблема в ядре Linux? Кто-нибудь пробовал mmap'ing один файл, используя MAP_SHARED больше, чем 2 ГБ и использовал (чтение / запись) успешно?

2 ответа

Решение

Я считаю, что проблема в том, что j является int, когда j поражает большие значения, (j + 1) * BLOCK_SIZE переполнения и ваш ftruncate Звонок не делает то, что вы собираетесь. Проверка возвращаемого значения из ftruncate должен подтвердить это.

Справочная страница mmap специально вызывает SIGBUS Это означает, что попытка доступа не поддерживается файлом.

Я не уверен, поможет ли это в вашем случае. Когда у нас были проблемы с большими файлами, помогло следующее. Мы поставили макрос:

#define _FILE_OFFSET_BITS 64

перед включением стандартных заголовков. Вы можете определить это также в командной строке при вызове gcc.

Другие вопросы по тегам