0

On Mac my use of munmap results in seeing higher page reclaims.

The return value of my munmap is 0, which indicates that the requested pages where successfully unmapped.

Why do I see higher page reclaims when I test programs using memory I have mapped and unmapped in this way?

Is there a way to debug munmap and see if my calls to that function aren't doing anything to the mapped memory that is passed to it.

I used "/usr/bin/time -l" to see the amount of page reclaims I get from running my program. Whenever I use munmap my page reclaims get higher then when I don't.

int           main(void)          
{                                                           
        int i = 0; char *addr;
        while (i < 1024)
        {
            addr = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
            addr[0] = 23;
            if (!munmap(addr, getpagesize()))
                print("Success\n");
            i++;
        }


        return (NULL);                                      
}

on allocation

when I call munmap:

  • I pass it the same pointer it gave me.
  • I check the return value and check if it is 0 <-- this is what I get most of the time.

I made a test program where I call mmap 1024 times and munmap that number of times too.

When I don't call munmap the reclaimed pages are within the region of 1478 and the value is the same when I call munmap.

How can I check if my use of that memory is correct?

1 Answers1

0

The important thing to remember about mmap is that the MAP_ANONYMOUS memory must be zeroed. So what happens usually is that a kernel will map a page frame with only zeroes in there - and only when a write hits the page, a read-write mapped zero page is mapped in place.

However, this is the reason why the kernel cannot reuse the originally mapped page right away - it does not know that only the first byte of the page is dirty - instead, it must zero all 4 kiB bytes on that page before it can be given back to the process in a new anonymous mapping. Hence in both examples there are at least 1024 page faults occurring.

If the memory would not need to be zeroed, Linux for example has an extra flag called MAP_UNINITIALIZED that tells kernel that the pages need not be zeroed, but it is only available in embedded devices:

MAP_UNINITIALIZED (since Linux 2.6.33)

Don't clear anonymous pages. This flag is intended to improve performance on embedded devices. This flag is honored only if the kernel was configured with the
CONFIG_MMAP_ALLOW_UNINITIALIZED option. Because of the security implications, that option is normally enabled only on embedded devices (i.e., devices where one has complete control of the contents of user memory).

I guess the reason for its non-availability in generic Linux kernels is because the kernel does not keep track of the process that previously had mapped the page frame, hence the page could leak information from a sensitive process.


bzeroing the page yourself would not affect performance - the kernel would not know that it was zeroed because there is no architecture that would support it in hardware - and then it is cheaper to write zeroes over the page than to check if the page is full of all zeroes and then in 99.9999999 % cases to write zeroes over it anyway.

  • Thank you for this. Would this trait carry over to freebsd systems such as Mac? Additionally, if I bzero this region myself again then would that result in the kernel having fewer page reclaims? – Gladwin Mohlamonyane Jul 17 '19 at 15:36
  • @GladwinMohlamonyane mostly... It comes from the POSIX semantics and from what the hardware is capable of. Of course other OSes can add a flag similar to `MAP_UNINITIALIZED`. It seems that OSX does not have any such flag. – Antti Haapala -- Слава Україні Jul 17 '19 at 16:23