8

I want to keep virtual address space reserved in my process for memory that was previously used but is not presently needed. I'm interested in the situation where the host kernel is Linux and it's configured to prevent overcommit (which it does by detailed accounting for all committed memory).

If I just want to prevent the data that my application is no longer using from occupying physical memory or getting swapped to disk (wasting resources either way), I can madvise the kernel that it's unneeded, or mmap new zero pages over top of it. But neither of these approaches will necessarily reduce the amount of memory that counts as committed, which other processes are then prevented from using.

What if I replace the pages with fresh zero pages that are marked read-only? My intent is that they don't count towards committed memory, and further that I can later use mprotect to make them writable, and that it would fail if making them writable would go over the committed memory limit. Is my understanding correct? Will this work?

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    A read-only page should not be considered in a process' commit charge (I don't have a reference at hand, so this is not an answer), and Linux provides the MAP_NORESERVE flag which should give you an even stronger guarantee. But I have to ask: why do you feel the need to reserve memory that's not in use? – Anon Dec 01 '10 at 21:56
  • Bad Things(tm) would happen if the same virtual addresses got allocated by `mmap` (at random) without the program being aware of it. :-) As for `MAP_NORESERVE`, I worry that might keep the pages from getting counted even after I `mprotect` them writable later. I suppose I could just `mmap`-over them again with new zero pages. – R.. GitHub STOP HELPING ICE Dec 01 '10 at 22:59
  • What bad things would happen? Why does your program need to never reuse address space it's previously used? That seems very unusual. – Angus Dec 01 '10 at 23:44
  • The pages are part of a larger contiguous data structure that are currently not in use. They will be in use again at some time in the future, unless the kernel cannot commit space of them, in which case I will handle the error condition. If the virtual addresses were unmapped, something else might get mapped there in the intervening time. This is a bit of a simplification, but gets across the basic point. – R.. GitHub STOP HELPING ICE Dec 02 '10 at 00:21
  • 3
    Since nobody with exact knowledge has answered, I think your best option is to write a test program that fills its virtual address space with read-only pages, and see how it affects swap. – Anon Dec 02 '10 at 15:16
  • Indeed. As a variant on this I could just disable swap and make a test program that fills 90% of the size of physical memory with data, then makes the same virtual size in read-only zero pages, and finally tries to `mprotect` them writable, in order to see if/where it fails. – R.. GitHub STOP HELPING ICE Dec 02 '10 at 16:40

2 Answers2

1

If you're not using the page (reading or writing to it), it won't be commited to your address space (only reserved).

But your address space is limited, so you can't play as you want/like with it.

See for example ElectricFence which may fail for large number of allocations, because of insertion of "nul page/guard page" (anonymous memory with no access). Have a look at these thread : "mprotect() failed: Cannot allocate memory" : http://thread.gmane.org/gmane.comp.lib.glibc.user/538/focus=976052

Yann Droneaud
  • 5,277
  • 1
  • 23
  • 39
1

On Linux, assuming overcommit has not been disabled, you can use the MAP_NORESERVE flag to mmap, which will ensure that the page in question will not be accounted as allocated memory prior to being accessed. If overcommit has been completely disabled, see below about multiple-mapping pages.

Note that Linux's behavior for zero pages has changed at times in the past; with some kernel versions, simply reading the page would cause it to be allocated. With others, a write is necessary. Note that the protection flags do not cause allocation directly; however they can prevent you from accidentally triggering an allocation. Therefore, for most reliable results you should avoid accessing the page at all by mprotecting with PROT_NONE.

As another, more portable option, you can map the same page at multiple locations. That is, create and open an empty temp file, unlink it, ftruncate to some reasonable number of pages, then mmap repeatedly at offset 0 into the file. This will absolutely guarantee the memory only counts once against your program's memory usage. You can even use MAP_PRIVATE to auto-reallocate it when you write to the page.

This may have higher memory usage than the MAP_NORESERVE technique (both for kernel tracking data, and for the pages of the temp file itself), however, so I would recommend using MAP_NORESERVE instead when available. If you do use this technique, try to make the region being mapped reasonably large (and put it in /dev/shm if on Linux, to avoid actual disk IO). Each individual mmap call will consume a certain amount of (non-swappable) kernel memory to track it, so it's good to keep that count down.

bdonlan
  • 224,562
  • 31
  • 268
  • 324