Some Name
Some Name

Reputation: 9540

Understanding memory allocations

I'm trying to understand "how memory works". As far as I understand the OS (Linux in my case) when calling mmap to create MAP_ANONYMOUS mapping it creates:

mmap() creates a new mapping in the virtual address space of the calling process

As far as I know virtyal address space of a process may exceed tge actual physical memory available.

Also as far as I know the actual mapping to a physical memory occurs when CPU triggers page fault when it tries to access to a memory page that is not in page table yet.

OS catches the page fault and creates an entry in a page directory.

What should happen if I mmaped some anonymous memory (but did not touch any of the pages), then other processess exhausted all the physical memory and then I try to use one of the pages mmaped (I have swap disabled)?

CPU should trigger page fault and then try to create an entry in a page direcrory. But since no physical memory left it will not be able to do so...

Upvotes: 4

Views: 416

Answers (2)

Luis Colorado
Luis Colorado

Reputation: 12708

First of all, if you disable swap (you don't add any swap partition) that doesn't mean you are not using swap. Read below.

You can run a system without any swap secondary space, but that doesn't mean you are not using virtual memory. You cannot disable virtual memory and virtual memory is a fundamental concept for the implementation of mmap(2) syscall.

mmap(2) uses a file to fill the initial contents of the pages it uses for the memory segment. But it does more... It uses normal virtual memory for the allocated pages of that segment, and swaps them out when the kernel needs some of those pages. As there's a file to store the page contents, you don't need to swap it, just write the contents of the page to the proper place in the file. As when other process that has the same shared memory segment attached, the same page is mapped on both processes, when one process writes the page, the other sees it immediately. Also if some process reads or writes the file, as the block used is the same one to read the disk file, the data it is going to see is the same data shared with both processes. This is how it works.

The kernel saves a lot of swap with this mechanism, and also this allows the kernel to be able to discard parts of the text segment of a program without having to swap them out to a secondary device (as they are already in the file's text segment for the program)

When you say

What should happen if I mmaped some anonymous memory (but did not touch any of the pages)...

if you didn't touch any of the pages, then probably neither of them have been still mapped actually, only the resource is prepared to be used, but has not yet allocated. It's when you fault on one of those pages (e.g. for reading, you promised not to touch them) is the page mapped to an actual memory page, but the disk backup (or the swap space for it) is actually in the file, not in the swap device. That page is also actually the disk block (more precisely the set of disk blocks) used to store the data from the disk driver, so no multiple copies of the same data are in use.

EDIT

Anonymous mmap(2) probably uses also disk blocks (in some default disk unit). So even in the case you don't use a swap device, probably you are allowed to use mmap(2) with virtual space mapped to disk inode. I have not checked this, but old unix pipes worked this way. A temporary inode (without an entry allocated in a directory, like erased files with an open process) can be used for this.

Upvotes: 2

bruno
bruno

Reputation: 32596

To use mmap (MAP_ANONYMOUS) or malloc changes nothing in your case, if you dont have enough free memory mmap returns MAP_FAILED and malloc returns NULL

If I use that program :

#include <sys/mman.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char ** argv)
{
  int n = atoi(argv[1]);
  void * m;

  if (argc == 1) {
    m = mmap(NULL, n*1024*1024, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

    if (m == MAP_FAILED) {
      puts("ko");
      return 0;
    }
  }
  else {
    m = malloc(n*1024*1024);
    if (m == 0) {
      puts("ko");
      return 0;
    }
  }

  puts("ok");
  getchar();

  char * p = (char *) m;
  char * sup = p + n*1024*1024;

  while (p < sup) {
    *p = 0;
    p += 512;
  }

  puts("done");
  getchar();

  return 0;
}

I am on a raspberrypi with 1Gb of memory and a swap of 100Mo, the memory is already used by chromium because I am on SO

proc/meminfo gives :

MemTotal:         949448 kB
MemFree:          295008 kB
MemAvailable:     633560 kB
Buffers:           39296 kB
Cached:           360372 kB
SwapCached:            0 kB
Active:           350416 kB
Inactive:         260960 kB
Active(anon):     191976 kB
Inactive(anon):    41908 kB
Active(file):     158440 kB
Inactive(file):   219052 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               352 kB
Writeback:             0 kB
AnonPages:        211704 kB
Mapped:           215924 kB
Shmem:             42304 kB
Slab:              24528 kB
SReclaimable:      12108 kB
SUnreclaim:        12420 kB
KernelStack:        2128 kB
PageTables:         5676 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    1675164 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB

If I do that :

pi@raspberrypi:/tmp $ ./a.out 750
ko

750 is to large, but

pi@raspberrypi:/tmp $ ./a.out 600 &
[1] 1525
pi@raspberrypi:/tmp $ ok

The used memory (top etc) doesn't reflect the 600Mo because I do not read/write in them

proc/meminfo gives :

MemTotal:         949448 kB
MemFree:          282860 kB
MemAvailable:     626016 kB
Buffers:           39432 kB
Cached:           362860 kB
SwapCached:            0 kB
Active:           362696 kB
Inactive:         260580 kB
Active(anon):     199880 kB
Inactive(anon):    41392 kB
Active(file):     162816 kB
Inactive(file):   219188 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:               624 kB
Writeback:             0 kB
AnonPages:        220988 kB
Mapped:           215672 kB
Shmem:             41788 kB
Slab:              24788 kB
SReclaimable:      12296 kB
SUnreclaim:        12492 kB
KernelStack:        2136 kB
PageTables:         5692 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2288564 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB

And I can again do

pi@raspberrypi:/tmp $ ./a.out 600 &
[2] 7088
pi@raspberrypi:/tmp $ ok

pi@raspberrypi:/tmp $ jobs
[1]-  stopped                 ./a.out 600
[2]+  stopped                 ./a.out 600
pi@raspberrypi:/tmp $ 

Even the total is too large for the memory + swap, /proc/meminfo gives :

MemTotal:         949448 kB
MemFree:          282532 kB
MemAvailable:     626112 kB
Buffers:           39432 kB
Cached:           359980 kB
SwapCached:            0 kB
Active:           365200 kB
Inactive:         257736 kB
Active(anon):     202280 kB
Inactive(anon):    38320 kB
Active(file):     162920 kB
Inactive(file):   219416 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:         102396 kB
Dirty:                52 kB
Writeback:             0 kB
AnonPages:        223520 kB
Mapped:           212600 kB
Shmem:             38716 kB
Slab:              24956 kB
SReclaimable:      12476 kB
SUnreclaim:        12480 kB
KernelStack:        2120 kB
PageTables:         5736 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2876612 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB

If I write in the memory of %1 then stop it I have a lot of swap done on the flash

pi@raspberrypi:/tmp $ %1
./a.out 600

done
^Z
[1]+  stopped                 ./a.out 600

now there is almost no free swap and almost no free memory, /proc/meminfo gives

MemTotal:         949448 kB
MemFree:           33884 kB
MemAvailable:      32544 kB
Buffers:             796 kB
Cached:            66032 kB
SwapCached:        66608 kB
Active:           483668 kB
Inactive:         390360 kB
Active(anon):     462456 kB
Inactive(anon):   374188 kB
Active(file):      21212 kB
Inactive(file):    16172 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:        102396 kB
SwapFree:           3080 kB
Dirty:                96 kB
Writeback:             0 kB
AnonPages:        740984 kB
Mapped:            61176 kB
Shmem:             29288 kB
Slab:              21932 kB
SReclaimable:       9084 kB
SUnreclaim:        12848 kB
KernelStack:        2064 kB
PageTables:         7012 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      577120 kB
Committed_AS:    2873112 kB
VmallocTotal:    1114112 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
CmaTotal:           8192 kB
CmaFree:            6796 kB

%1 is still waiting on the getchar, if I do the same for %2 it works but in fact because the process %1 disappear (without message on the shell)

The behavior is the same if I malloc (giving a second argument to the program)


See also What is the purpose of MAP_ANONYMOUS flag in mmap system call?

Upvotes: 2

Related Questions