73

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?

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Emer
  • 3,734
  • 2
  • 33
  • 47
  • 4
    you need to open the file with the `O_LARGEFILE` flag. check manual. unsure if >4GB files can be `mmap`ed. – phoxis Aug 28 '11 at 16:35
  • 2
    Can't reproduce here. Your code runs just fine on a 9G file. How much (RAM+SWAP) do you have? What's your current /proc/sys/vm/overcommit_memory policy? – Mat Aug 28 '11 at 16:42
  • @Mat $free -m #Mem{ total: 1984, used: 1923, free 60} Swap{ total: 2021, used: 0, free: 2021} $ cat /proc/sys/vm/overcommit_memory #returns 0 – Emer Aug 28 '11 at 16:52
  • 2
    @phoxis that flag would be required from a 32 bit machine. [Ref](http://www.kernel.org/doc/man-pages/online/pages/man2/open.2.html) – Emer Aug 28 '11 at 17:00
  • @Emer: sorry, i just noticed the `x86_64` – phoxis Aug 28 '11 at 17:01
  • Looks like you've got the answer for your particular problem, but this error might also be caused by have `ulimit -v` set too low for the amount of memory you are requesting, regardless of how much memory/swap you have. – Chris Dodd Aug 28 '11 at 17:30
  • Did you try running `strace` to see if you're stumbling on an ENOMEM? – susmits Aug 29 '11 at 11:10
  • This question should be removed for using an indention of three! – Bouncner Oct 04 '22 at 20:12

3 Answers3

80

MAP_PRIVATE mappings 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 a MAP_SHARED mapping 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 with O_RDONLY - this itself may be another problem for you; you must specify O_RDWR if you want to use PROT_WRITE with MAP_SHARED.

As for PROT_WRITE only, this happens to work on x86, because x86 doesn't support write-only mappings, but may cause segfaults on other platforms. Request PROT_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_SHARED results in an EPERM error (since I'm not allowed to write to the backing file opened with O_RDONLY). Changing to PROT_READ and MAP_SHARED allows 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, munmap and remap with MAP_PRIVATE the 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 1 to /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.

bdonlan
  • 224,562
  • 31
  • 268
  • 324
  • hmm.. Mat managed to map 8*3G (23G) with 16G of virtual space. So it doesn't really reserve memory.. – Karoly Horvath Aug 28 '11 at 17:18
  • 1
    @yi_H, the linux kernel by default allows you to exceed physical+swap by a certain percentage: http://opsmonkey.blogspot.com/2007/01/linux-memory-overcommit.html Depending on your system configuration however you might have things set to a more strict configuration. Or if you only have, say, 4G total, you may exceed the limit. – bdonlan Aug 28 '11 at 17:21
  • I am aware of that, check what he wrote. – Karoly Horvath Aug 28 '11 at 17:24
  • Yes, I noticed the O_RDONLY and the PROT_WRITE. However, I need to swap bytes of the mapped file without modifying it. I tried with PROT_READ and MAP_PRIVATE and did not work. Thank you for the answer – Emer Aug 28 '11 at 17:33
  • @Emer, see my edit. If you need to swap 8GB of file, you'll need 8GB of memory+swap to store the result. – bdonlan Aug 28 '11 at 17:36
  • @yi_H: Mat probably has the kernel set to "heuristic overcommit", in which case very large ("obviously bad") mappings can fail. – caf Aug 29 '11 at 04:57
  • Thanks, had the same problem: only 1 GiB of RAM on an amd64 machine, so I wanted to use mmap of a file to access 32 GiB of virtual memory without swapping the machine to death, and it turned out MAP_SHARED was the key. – mirabilos Nov 23 '13 at 14:50
5

Linux (and apparently a few other UNIX systems) have the MAP_NORESERVE flag for mmap(2), which can be used to explicitly enable swap space overcommitting. This can be useful when you wish to map a file larger than the amount of free memory available on your system.

This is particularly handy when used with MAP_PRIVATE and only intend to write to a small portion of the memory mapped range, since this would otherwise trigger swap space reservation of the entire file (or cause the system to return ENOMEM, if system wide overcommitting hasn't been enabled and you exceed the free memory of the system).

The issue to watch out for is that if you do write to a large portion of this memory, the lazy swap space reservation may cause your application to consume all the free RAM and swap on the system, eventually triggering the OOM killer (Linux) or causing your app to receive a SIGSEGV.

dcoles
  • 3,785
  • 2
  • 28
  • 26
4

You don't have enough virtual memory to handle that mapping.

As an example, I have a machine here with 8G RAM, and ~8G swap (so 16G total virtual memory available).

If I run your code on a VirtualBox snapshot that is ~8G, it works fine:

$ 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 

Now, if I drop the swap, I'm left with 8G total memory. (Don't run this on an active server.) And the result is:

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

So make sure you have enough virtual memory to hold that mapping (even if you only touch a few pages in that file).

Mat
  • 202,337
  • 40
  • 393
  • 406
  • Can you check whether you can map two 8G files with 8+8 virtual space? it sounds a bit weird that it needs the space even though some pages might never gets loaded or modified (in which case they can be simply dropped). – Karoly Horvath Aug 28 '11 at 17:01
  • I can `mmap` many files smaller than 16G with (8G+8G) virtual memory available (went up to ~ 23G with three files). I can't map a single >8G file with just the 8G. – Mat Aug 28 '11 at 17:11
  • Thank you for testing it. Is there a way to calculate the exact amount of virtual memory needed in order to map a file? or it would depend of many factors? – Emer Aug 28 '11 at 17:36
  • 2
    @Mat, 0 is not 'always overcommit'; 1 is – bdonlan Aug 28 '11 at 17:52
  • @bdonlan: you are right, sorry - that whole comment was wrong – Mat Aug 28 '11 at 17:57