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?
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.
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:
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.
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.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With