问题
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?
回答1:
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);
}
来源:https://stackoverflow.com/questions/22416989/change-an-mmapd-memory-region-from-map-shared-to-map-private