I am trying to “mmap” a binary file (~ 8Gb) using the following code (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 is compiled using gcc -std=c99 test.c -o test and file of test returns: test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped
Although this works fine for small files, I get a segmentation fault when I try to load a big one. The program actually returns:
Size: 8274324021
mmap: Cannot allocate memory
I managed to map the whole file using boost::iostreams::mapped_file but I want to do it using C and system calls. What is wrong with my code?
MAP_PRIVATEmappings require a memory reservation, as writing to these pages may result in copy-on-write allocations. This means that you can’t map something too much larger than your physical ram + swap. Try using aMAP_SHAREDmapping instead. This means that writes to the mapping will be reflected on disk – as such, the kernel knows it can always free up memory by doing writeback, so it won’t limit you.I also note that you’re mapping with
PROT_WRITE, but you then go on and read from the memory mapping. You also opened the file withO_RDONLY– this itself may be another problem for you; you must specifyO_RDWRif you want to usePROT_WRITEwithMAP_SHARED.As for
PROT_WRITEonly, this happens to work on x86, because x86 doesn’t support write-only mappings, but may cause segfaults on other platforms. RequestPROT_READ|PROT_WRITE– or, if you only need to read,PROT_READ.On my system (VPS with 676MB RAM, 256MB swap), I reproduced your problem; changing to
MAP_SHAREDresults in anEPERMerror (since I’m not allowed to write to the backing file opened withO_RDONLY). Changing toPROT_READandMAP_SHAREDallows the mapping to succeed.If you need to modify bytes in the file, one option would be to make private just the ranges of the file you’re going to write to. That is,
munmapand remap withMAP_PRIVATEthe areas you intend to write to. Of course, if you intend to write to the entire file then you need 8GB of memory to do so.Alternately, you can write
1to/proc/sys/vm/overcommit_memory. This will allow the mapping request to succeed; however, keep in mind that if you actually try to use the full 8GB of COW memory, your program (or some other program!) will be killed by the OOM killer.