Mmap () весь большой файл - PullRequest
       6

Mmap () весь большой файл

63 голосов
/ 28 августа 2011

Я пытаюсь «отобразить» двоичный файл (~ 8 ГБ), используя следующий код (test.c).

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

#define handle_error(msg) \
  do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
   const char *memblock;
   int fd;
   struct stat sb;

   fd = open(argv[1], O_RDONLY);
   fstat(fd, &sb);
   printf("Size: %lu\n", (uint64_t)sb.st_size);

   memblock = mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
   if (memblock == MAP_FAILED) handle_error("mmap");

   for(uint64_t i = 0; i < 10; i++)
   {
     printf("[%lu]=%X ", i, memblock[i]);
   }
   printf("\n");
   return 0;
}

test.c скомпилирован с использованием gcc -std=c99 test.c -o test и file изтест возвращает: test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

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

Size: 8274324021 
mmap: Cannot allocate memory

Мне удалось отобразить весь файл, используя boost :: iostreams :: mapped_file, но я хочу сделать это, используя C и системные вызовы.Что не так с моим кодом?

Ответы [ 3 ]

63 голосов
/ 28 августа 2011

MAP_PRIVATE для отображения требуется резервирование памяти, поскольку запись на эти страницы может привести к выделению при копировании при записи. Это означает, что вы не можете отобразить что-то слишком большое, чем ваш физический баран + своп. Попробуйте вместо этого использовать отображение MAP_SHARED. Это означает, что запись в отображение будет отражена на диске - поэтому ядро ​​знает, что оно всегда может освободить память, выполняя обратную запись, поэтому оно не будет ограничивать вас.

Я также отмечаю, что вы сопоставляете с PROT_WRITE, но затем продолжаете и читаете из сопоставления памяти. Вы также открыли файл с O_RDONLY - это само по себе может быть другой проблемой для вас; Вы должны указать O_RDWR, если хотите использовать PROT_WRITE с MAP_SHARED.

Только для PROT_WRITE это работает на x86, потому что x86 не поддерживает сопоставления только для записи, но может вызвать ошибки на других платформах. Запрос PROT_READ|PROT_WRITE - или, если вам нужно только прочитать, PROT_READ.

В моей системе (VPS с 676 МБ ОЗУ, 256 МБ подкачки) я воспроизвел вашу проблему; изменение на MAP_SHARED приводит к ошибке EPERM (поскольку я не могу записать в файл поддержки, открытый с O_RDONLY). Изменение на PROT_READ и MAP_SHARED позволяет отображению быть успешным.

Если вам нужно изменить байты в файле, одним из вариантов будет сделать приватными только диапазоны файла, в который вы собираетесь записать. То есть munmap и переназначить MAP_PRIVATE областей, в которые вы собираетесь писать. Конечно, если вы намереваетесь записать в весь файл , вам потребуется 8 ГБ памяти для этого.

Альтернативно, вы можете написать 1 в /proc/sys/vm/overcommit_memory. Это позволит выполнить запрос сопоставления; однако имейте в виду, что если вы на самом деле попытаетесь использовать все 8 ГБ памяти COW, ваша программа (или другая программа!) будет убита убийцей OOM.

4 голосов
/ 04 декабря 2012

Linux (и, по-видимому, некоторые другие системы UNIX) имеют флаг MAP_NORESERVE для mmap (2) , который можно использовать для явного включения избыточной загрузки пространства подкачки. Это может быть полезно, если вы хотите отобразить файл, размер которого превышает объем свободной памяти, доступной в вашей системе.

Это особенно удобно при использовании с MAP_PRIVATE и предназначено только для записи в небольшую часть диапазона отображения памяти, поскольку в противном случае это приведет к резервированию пространства подкачки всего файла (или к тому, что система вернет ENOMEM , если перегрузка всей системы не была включена, и вы превысили свободную память системы).

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

4 голосов
/ 28 августа 2011

У вас недостаточно виртуальной памяти для обработки этого сопоставления.

Например, у меня есть машина с 8G RAM и подкачкой ~ 8G (так что доступно всего 16G виртуальной памяти).

Если я запускаю ваш код на снимке VirtualBox размером ~ 8G, он работает нормально:

$ ls -lh /media/vms/.../snap.vdi
-rw------- 1 me users 9.2G Aug  6 16:02 /media/vms/.../snap.vdi
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256 
[0]=3C [1]=3C [2]=3C [3]=20 [4]=4F [5]=72 [6]=61 [7]=63 [8]=6C [9]=65 

Теперь, если я отбросу своп, у меня останется 8G общего объема памяти.( Не запускайте это на активном сервере.) И результат:

$ sudo swapoff -a
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256 
mmap: Cannot allocate memory

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

...