Mmap () весь большой файл
Я пытаюсь "mmap" двоичный файл (~8 ГБ), используя следующий код (тест.с.)
#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: %lun", (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;
}
3 ответов:
MAP_PRIVATEсопоставления требуют резервирования памяти, так как запись на эти страницы может привести к распределению копирования при записи. Это означает, что вы не можете сопоставить что-то слишком большое, чем ваш физический ram + swap. Попробуйте использоватьMAP_SHAREDсопоставление. Это означает, что запись в сопоставление будет отражена на диске - таким образом, ядро знает, что оно всегда может освободить память, выполнив обратную запись, поэтому оно не будет ограничивать вас.Я также отмечаю, что вы сопоставляете с
PROT_WRITE, но ты тогда иди и читать с карт памяти. Вы также открыли файл с помощьюO_RDONLY- это само по себе может быть еще одна проблема для вас; вы должны указатьO_RDWRесли вы хотите использоватьPROT_WRITEСMAP_SHARED.как
PROT_WRITEтолько это работает на x86, потому что x86 не поддерживает сопоставления только для записи, но может вызвать segfaults на других платформах. ЗапросPROT_READ|PROT_WRITE- или, если вам нужно только читать,PROT_READ.на моей системе (VPS с 676MB RAM, 256MB swap), я воспроизвел ваш проблема; изменение на
MAP_SHAREDрезультатEPERMошибка (так как мне не разрешено писать в файл резервного копирования, открытый с помощьюO_RDONLY). Изменение наPROT_READиMAP_SHAREDпозволяет успешно выполнить сопоставление.Если вам нужно изменить байты в файле, одним из вариантов было бы сделать частными только диапазоны файла, в который вы собираетесь писать. То есть,
munmapи сопоставить сMAP_PRIVATEобласти, в которые вы собираетесь писать. Конечно, если вы намерены писать весь файл тогда вам нужно 8 ГБ памяти, чтобы сделать это.можно писать
1до/proc/sys/vm/overcommit_memory. Это позволит выполнить запрос на сопоставление; однако имейте в виду, что если вы действительно попытаетесь использовать полный 8 ГБ памяти COW, ваша программа (или какая-либо другая программа!) будет убит убийцей ООМ.
у вас недостаточно виртуальной памяти для обработки этого сопоставления.
например, у меня есть машина здесь с 8G RAM и ~8G swap (так что доступна общая виртуальная память 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поэтому убедитесь, что у вас достаточно виртуальная память для хранения этого сопоставления (даже если вы касаетесь только нескольких страниц в этом файле).
Linux (и, по-видимому, несколько других систем UNIX) имеют
MAP_NORESERVEфлаг mmap (2), который может быть использован для явного включения переполнения пространства подкачки. Это может быть полезно, когда вы хотите файл, размер которого превышает объем свободной памяти, доступной в системе.Это особенно удобно при использовании
MAP_PRIVATEи только намереваются записать в небольшую часть диапазона отображения памяти, так как в противном случае это вызовет резервирование пространства подкачки всего файл (или заставить систему вернутьENOMEM, Если общесистемная перегрузка не была включена, и вы превышаете свободную память системы).проблема заключается в том, что если вы пишете в большую часть этой памяти, ленивое резервирование пространства подкачки может привести к тому, что ваше приложение будет потреблять всю свободную оперативную память и подкачку в системе, в конечном итоге вызывая OOM killer (Linux) или заставляя ваше приложение получать
SIGSEGV.
Comments