Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change an mmap'd memory region from MAP_SHARED to MAP_PRIVATE

Tags:

c

mmap

So I have a region of memory that I have allocated with mmap() similar to the code below:

void * ptr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);

The key here is that I'm using the MAP_SHARED flag. Unlike this related question where it was sufficient to simply call mmap() again to get MAP_PRIVATE and Copy-on-Write semantics, I can't have the kernel allocate me a different range of virtual addresses. In addition, I do not want to invoke munmap() and risk the kernel giving part/all of that address range to something else within the process before I can call mmap() again.

Is there an existing mechanism to switch a region of mmap'd memory from MAP_SHARED to MAP_PRIVATE to get copy-on-write semantics without unmapping the block?

like image 376
Phoenios Avatar asked Mar 14 '14 22:03

Phoenios


People also ask

Are changes made to the file after mmap () visible in the mapped region?

It is unspecified whether changes made to the file after the mmap () call are visible in the mapped region. Both MAP_SHARED and MAP_PRIVATE are described in POSIX.1-2001 and POSIX.1-2008. MAP_SHARED_VALIDATE is a Linux extension.

What is MMAP() in C++?

The mmap () function shall establish a mapping between a process' address space and a file, shared memory object, or typed memory object. The format of the call is as follows:

What is the difference between mmap and munmap in Android?

It kind of is the backbone of shared memory in Android. mmap () creates a new mapping in the virtual address space of the calling process. If you check out the Linux kernel page for mmap you’ll see several arguments and flags. On the other hand, munmap () is used to free the allocated memory.

What determines the area of memory to clear in mmap?

The length determines the area of memory to clear. The area of memory from addr to addr + length would be freed on this call. MMAP can be thought of as the core memory allocation API in Linux and several high-level constructs take advantage of this for providing various features.


1 Answers

Calling mmap() again with MAP_PRIVATE | MAP_FIXED will work. The MMAP(2) man page states that when using MAP_FIXED:

If the specified address cannot be used, mmap() will fail.

So, just use a temporary pointer to store the mmap() result. If mmap() fails, no harm done. If mmap() succeeds you have successfully switched a memory mapped region from MAP_SHARED to MAP_PRIVATE. (see example)

#include <stdio.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main()
{
    int fd;
    void *shared_0, *shared_1;
    void *private_0;
    struct stat st;

    if((fd = open("filename", O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
        fprintf(stderr, "Failed to open(): %s\n", strerror(errno));
    }
    else if(fstat(fd, &st) < 0) {
        fprintf(stderr, "Failed fstat(): %s\n", strerror(errno));
    }
    else if((shared_0 = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, 0)) == MAP_FAILED) {
        fprintf(stderr, "Failed to mmap(): %s\n", strerror(errno));
    }
    else if((shared_1 = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, 0)) == MAP_FAILED) {
        fprintf(stderr, "Failed to mmap(): %s\n", strerror(errno));
    }
    else if((private_0 = mmap(shared_0, st.st_size, PROT_READ | PROT_WRITE,
            MAP_FIXED | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
        fprintf(stderr, "Failed to mmap(): %s\n", strerror(errno));
    }
    else if(shared_0 != private_0) {
        fprintf(stderr, "Error: mmap() didn't map to the same region");
    }
    else {
        printf("shared_0: %p == private_0: %p\n", shared_0, private_0);
        printf("shared_1: %p\n", shared_1);

        printf("Shared mapping before write: %d\n", (*(char *)shared_1));
        printf("Private mapping before write: %d\n", (*(char *)private_0));

        /* write to the private COW mapping and sync changes */
        (*(char*)private_0) = 42;
        if(msync(private_0, 1, MS_SYNC | MS_INVALIDATE) < 0) {
            fprintf(stderr, "Failed msync(): %s\n", strerror(errno));
            return(1);
        }

        printf("Shared mapping after write: %d\n", (*(char *)shared_1));
        printf("Private mapping after write: %d\n", (*(char *)private_0));
    }

    return(0);
}
like image 54
pughar Avatar answered Nov 15 '22 07:11

pughar