2015-03-23 2 views
6

Я пытаюсь создать карту памяти огромного файла (около 100 ГБ), чтобы сохранить B-Tree с миллиардами пар ключ-значение. Память маленькая, чтобы хранить все данные в памяти, поэтому я пытаюсь отобразить файл с диска и вместо использования malloc я возвращаю и увеличиваю указатель на отображаемую область.C - Карта памяти B-Tree

#define MEMORY_SIZE 300000000 

unsigned char *mem_buffer; 
void *start_ptr; 

void *my_malloc(int size) { 
    unsigned char *ptr = mem_buffer; 
    mem_buffer += size; 

    return ptr; 
} 

void *my_calloc(int size, int object_size) { 
    unsigned char *ptr = mem_buffer; 
    mem_buffer += (size * object_size); 

    return ptr; 
} 

void init(const char *file_path) { 
    int fd = open(file_path, O_RDWR, S_IREAD | S_IWRITE); 

    if (fd < 0) { 
     perror("Could not open file for memory mapping"); 
     exit(1); 
    } 

    start_ptr = mmap(NULL, MEMORY_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 
    mem_buffer = (unsigned char *) start_ptr; 

    if (mem_buffer == MAP_FAILED) { 
     perror("Could not memory map file"); 
     exit(1); 
    } 

    printf("Successfully mapped file.\n"); 
} 

void unmap() { 
    if (munmap(start_ptr, MEMORY_SIZE) < 0) { 
     perror("Could not unmap file"); 
     exit(1); 
    } 

    printf("Successfully unmapped file.\n"); 
} 

Основной метод:

int main(int argc, char **argv) { 

    init(argv[1]); 

    unsigned char *arr = (unsigned char *) my_malloc(6); 
    arr[0] = 'H'; 
    arr[1] = 'E'; 
    arr[2] = 'L'; 
    arr[3] = 'L'; 
    arr[4] = 'O'; 
    arr[5] = '\0'; 

    unsigned char *arr2 = (unsigned char *) my_malloc(5); 
    arr2[0] = 'M'; 
    arr2[1] = 'I'; 
    arr2[2] = 'A'; 
    arr2[3] = 'U'; 
    arr2[4] = '\0'; 

    printf("Memory mapped string1: %s\n", arr); 
    printf("Memory mapped string2: %s\n", arr2); 

    struct my_btree_node *root = NULL; 

    insert(&root, arr, 10); 
    insert(&root, arr2, 20); 

    print_tree(root, 0, false); 

// cin.ignore(); 

    unmap(); 

    return EXIT_SUCCESS; 
} 

Проблема заключается в том, что я получаю Cannot allocate memory (ERRNO 12), если запрашиваемый размер больше, чем фактическая память или Segmentation fault если запрашиваемое пространство находится за пределами отображаемой области. Мне сказали, что можно сопоставлять файлы больше, чем фактическая память.

Будет ли система управлять файлом сама по себе, или я несу ответственность за отображение только объема свободной памяти, а при доступе к дальнейшему пространству я должен размонтировать и сопоставить другое смещение.

Спасибо

EDIT

ОС: Ubuntu 14.04 LTS x86_64

бен/Стиральная машина: ELF 64-бит LSB исполняемым, x86-64, версия 1 (SYSV), динамически связаны (использует общие библиотеки), для GNU/Linux 2.6.24, BuildID [sha1] = 9dc831c97ce41b0c6a77b639121584bf76deb47d, не разделяется

+0

C/C++ - это не язык. Пожалуйста, будьте более конкретными. – dandan78

+0

@ dandan78 Извините. Готово. – aQuip

+2

http://stackoverflow.com/questions/7222164/mmap-an-entire-large-file –

ответ

9

Во-первых, убедитесь, что вы работаете на 64-битном процессоре в 64-разрядном режиме. На 32-битном процессоре адресное пространство вашего процесса составляет всего 2 байтов (четыре гигабайта), и нет возможности установить 100 ГБ в это все сразу - просто адресов недостаточно. (Кроме того, большой фрагмент этого адресного пространства уже будет использоваться другими сопоставлениями или зарезервирован ядром.)

Во-вторых, могут возникнуть проблемы, даже если отображение вписывается в адресное пространство. Память, которая отображается в ваш процесс (это также включает в себя, например,сегменты кода и данных вашей программы, а также для разделяемых библиотек) разделяется на единицы страниц (обычно по 4 КБ на каждый х86), где каждая страница требует некоторых метаданных в ядре и MMU. Это еще один ресурс, который может быть исчерпан при создании огромных сопоставлений памяти.

Как предложено в Mmap() an entire large file, вы можете попробовать использовать MAP_SHARED. Это может позволить ядру распределять память для отображения лениво, поскольку к ним поступают страницы, поскольку он знает, что он всегда может поменять страницу на файл на диске, если есть нехватка памяти. С MAP_PRIVATE ядро ​​должно выделять новую страницу каждый раз, когда страница изменяется (поскольку это изменение не должно выполняться), что небезопасно делать лениво, если в системе заканчивается память и своп.

Вам также может потребоваться передать MAP_NORESERVE в mmap() при выделении памяти больше, чем есть физическая память, или установить /proc/sys/vm/overcommit_memory (см proc(5)) до 1 (который немного некрасиво из-за того общесистемным, хотя).

В моей системе, которая похожа на вашу с 8 ГБ оперативной памяти и 8 Гбайт свопа, MAP_SHARED одного достаточно, чтобы mmap() - файл размером 40 ГБ. MAP_PRIVATE вместе с MAP_NORESERVE работает тоже.

Если это не сработает, вы, вероятно, столкнетесь с ограничением MMU. Многие современные архитектуры процессоров поддерживают huge pages, которые превышают размер страницы по умолчанию. Точка огромных страниц заключается в том, что вам нужно меньше страниц для сопоставления того же объема памяти (при условии большого сопоставления), что уменьшает количество метаданных и может сделать перевод адресов и контекстные переключатели более эффективными. Недостатком огромных страниц является уменьшение гранулярности отображения и увеличение потерь (внутренняя фрагментация), когда используется только небольшая часть страницы.

Сопряжение MAP_SHARED и некоторые случайные файлы с огромными страницами вряд ли будут работать, кстати (в случае, если для устранения проблемы недостаточно MAP_SHARED). Файл должен быть в hugetlbfs.

Передача распределения MAP_HUGETLB в mmap() запросов с использованием огромных страниц (хотя это может быть просто для анонимных отображений, где также seems что огромные страницы должны быть автоматическими на многих системах в настоящее время). Возможно, вам также может понадобиться обмануть /proc/sys/vm/nr_hugepages и /proc/sys/vm/nr_overcommit_hugepages - см. this thread и файл Documentation/vm/hugetlbpage.txt в источниках ядра.

Остерегайтесь проблем с выравниванием при написании собственного распределителя памяти. Надеюсь, это не слишком вяло, но см. this answer.

В качестве примечания, любая память, которую вы получаете из файла с отображением памяти, должна фактически существовать в файле. Если файл меньше, чем сопоставление, и вы хотите иметь доступ к дополнительной памяти, вы можете сначала увеличить файл, используя ftruncate(2). (Это может не сильно увеличить размер на диске, если файловая система поддерживает sparse files с файловыми отверстиями.)

0

Не зная, в какой операционной системе вы находитесь, лучшее, что у меня есть, это то, что у вас есть рейтинговая система не допускает неограниченное превышение памяти или подсчитывает MAP_PRIVATE сопоставлений с RLIMIT_DATA ulimit. Оба означают, что ваш код не будет работать.

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

Решение должно использовать MAP_SHARED, но убедитесь, что вы понимаете страницу руководства для mmap и что делает MAP_SHARED. Кроме того, убедитесь, что вы либо отображаете только размер файла, либо ftruncate, чтобы файл был таким большим, насколько вам это нужно.

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

Смежные вопросы