Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Your question is a little confusing. From what I understood, this code will do what you need:</p> <pre><code>#define PAGESIZE 4096 #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;string.h&gt; #include &lt;sys/mman.h&gt; #include &lt;errno.h&gt; #include &lt;sys/types.h&gt; #include &lt;fcntl.h&gt; #include &lt;unistd.h&gt; #include &lt;assert.h&gt; struct StoredObject { int IntVal; char StrVal[25]; }; int main(int argc, char **argv) { int fd = open("mmapfile", O_RDWR | O_CREAT | O_TRUNC, (mode_t) 0600); //Set the file to the size of our data (2 pages) lseek(fd, PAGESIZE*2 - 1, SEEK_SET); write(fd, "", 1); //The final byte unsigned char *mapPtr = (unsigned char *) mmap(0, PAGESIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); struct StoredObject controlObject; controlObject.IntVal = 12; strcpy(controlObject.StrVal, "Mary had a little lamb.\n"); struct StoredObject *mary1; mary1 = (struct StoredObject *)(mapPtr + PAGESIZE - 4); //Will fall on the boundary between first and second page memcpy(mary1, &amp;controlObject, sizeof(StoredObject)); printf("%d, %s", mary1-&gt;IntVal, mary1-&gt;StrVal); //Should print "12, Mary had a little lamb.\n" struct StoredObject *john1; john1 = mary1 + 1; //Comes immediately after mary1 in memory; will start and end in the second page memcpy(john1, &amp;controlObject, sizeof(StoredObject)); john1-&gt;IntVal = 42; strcpy(john1-&gt;StrVal, "John had a little lamb.\n"); printf("%d, %s", john1-&gt;IntVal, john1-&gt;StrVal); //Should print "12, Mary had a little lamb.\n" //Make sure the data's on the disk, as this is the initial, "read-only" data msync(mapPtr, PAGESIZE * 2, MS_SYNC); //This is the inital data set, now in memory, loaded across two pages //At this point, someone could be reading from there. We don't know or care. //We want to modify john1, but don't want to write over the existing data //Easy as pie. //This is the shadow map. COW-like optimization will take place: //we'll map the entire address space from the shared source, then overlap with a new map to modify //This is mapped anywhere, letting the system decide what address we'll be using for the new data pointer unsigned char *mapPtr2 = (unsigned char *) mmap(0, PAGESIZE * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //Map the second page on top of the first mapping; this is the one that we're modifying. It is *not* backed by disk unsigned char *temp = (unsigned char *) mmap(mapPtr2 + PAGESIZE, PAGESIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANON, 0, 0); if (temp == MAP_FAILED) { printf("Fixed map failed. %s", strerror(errno)); } assert(temp == mapPtr2 + PAGESIZE); //Make a copy of the old data that will later be changed memcpy(mapPtr2 + PAGESIZE, mapPtr + PAGESIZE, PAGESIZE); //The two address spaces should still be identical until this point assert(memcmp(mapPtr, mapPtr2, PAGESIZE * 2) == 0); //We can now make our changes to the second page as needed struct StoredObject *mary2 = (struct StoredObject *)(((unsigned char *)mary1 - mapPtr) + mapPtr2); struct StoredObject *john2 = (struct StoredObject *)(((unsigned char *)john1 - mapPtr) + mapPtr2); john2-&gt;IntVal = 52; strcpy(john2-&gt;StrVal, "Mike had a little lamb.\n"); //Test that everything worked OK assert(memcmp(mary1, mary2, sizeof(struct StoredObject)) == 0); printf("%d, %s", john2-&gt;IntVal, john2-&gt;StrVal); //Should print "52, Mike had a little lamb.\n" //Now assume our garbage collection routine has detected that no one is using the original copy of the data munmap(mapPtr, PAGESIZE * 2); mapPtr = mapPtr2; //Now we're done with all our work and want to completely clean up munmap(mapPtr2, PAGESIZE * 2); close(fd); return 0; } </code></pre> <hr> <p>My modified answer should address your safety concerns. Only use <code>MAP_FIXED</code> on the second <code>mmap</code> call (like I have above). The cool thing about <code>MAP_FIXED</code> is that it lets you overwrite an existing <code>mmap</code> address section. It'll unload the range you're overlapping and replace it with your new mapped content:</p> <pre><code> MAP_FIXED [...] If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. [...] </code></pre> <p>This way, you let the OS take care of finding a contiguous memory block of hundreds of megs for you (never call <code>MAP_FIXED</code> on address you don't know for sure isn't available). Then you call <code>MAP_FIXED</code> on a subsection of that now-mapped huge space with the data that you will be modifying. Tada.</p> <hr> <p>On Windows, something like this should work (I'm on a Mac at the moment, so untested):</p> <pre><code>int main(int argc, char **argv) { HANDLE hFile = CreateFile(L"mmapfile", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); //Set the file to the size of our data (2 pages) SetFilePointer(hFile, PAGESIZE*2 - 1, 0, FILE_BEGIN); DWORD bytesWritten = -1; WriteFile(hFile, "", 1, &amp;bytesWritten, NULL); HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, PAGESIZE * 2, NULL); unsigned char *mapPtr = (unsigned char *) MapViewOfFile(hMap, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, PAGESIZE * 2); struct StoredObject controlObject; controlObject.IntVal = 12; strcpy(controlObject.StrVal, "Mary had a little lamb.\n"); struct StoredObject *mary1; mary1 = (struct StoredObject *)(mapPtr + PAGESIZE - 4); //Will fall on the boundary between first and second page memcpy(mary1, &amp;controlObject, sizeof(StoredObject)); printf("%d, %s", mary1-&gt;IntVal, mary1-&gt;StrVal); //Should print "12, Mary had a little lamb.\n" struct StoredObject *john1; john1 = mary1 + 1; //Comes immediately after mary1 in memory; will start and end in the second page memcpy(john1, &amp;controlObject, sizeof(StoredObject)); john1-&gt;IntVal = 42; strcpy(john1-&gt;StrVal, "John had a little lamb.\n"); printf("%d, %s", john1-&gt;IntVal, john1-&gt;StrVal); //Should print "12, Mary had a little lamb.\n" //Make sure the data's on the disk, as this is the initial, "read-only" data //msync(mapPtr, PAGESIZE * 2, MS_SYNC); //This is the inital data set, now in memory, loaded across two pages //At this point, someone could be reading from there. We don't know or care. //We want to modify john1, but don't want to write over the existing data //Easy as pie. //This is the shadow map. COW-like optimization will take place: //we'll map the entire address space from the shared source, then overlap with a new map to modify //This is mapped anywhere, letting the system decide what address we'll be using for the new data pointer unsigned char *reservedMem = (unsigned char *) VirtualAlloc(NULL, PAGESIZE * 2, MEM_RESERVE, PAGE_READWRITE); HANDLE hMap2 = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, PAGESIZE, NULL); unsigned char *mapPtr2 = (unsigned char *) MapViewOfFileEx(hMap2, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, PAGESIZE, reservedMem); //Map the second page on top of the first mapping; this is the one that we're modifying. It is *not* backed by disk unsigned char *temp = (unsigned char *) MapViewOfFileEx(hMap2, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, PAGESIZE, reservedMem + PAGESIZE); if (temp == NULL) { printf("Fixed map failed. 0x%x\n", GetLastError()); return -1; } assert(temp == mapPtr2 + PAGESIZE); //Make a copy of the old data that will later be changed memcpy(mapPtr2 + PAGESIZE, mapPtr + PAGESIZE, PAGESIZE); //The two address spaces should still be identical until this point assert(memcmp(mapPtr, mapPtr2, PAGESIZE * 2) == 0); //We can now make our changes to the second page as needed struct StoredObject *mary2 = (struct StoredObject *)(((unsigned char *)mary1 - mapPtr) + mapPtr2); struct StoredObject *john2 = (struct StoredObject *)(((unsigned char *)john1 - mapPtr) + mapPtr2); john2-&gt;IntVal = 52; strcpy(john2-&gt;StrVal, "Mike had a little lamb.\n"); //Test that everything worked OK assert(memcmp(mary1, mary2, sizeof(struct StoredObject)) == 0); printf("%d, %s", john2-&gt;IntVal, john2-&gt;StrVal); //Should print "52, Mike had a little lamb.\n" //Now assume our garbage collection routine has detected that no one is using the original copy of the data //munmap(mapPtr, PAGESIZE * 2); mapPtr = mapPtr2; //Now we're done with all our work and want to completely clean up //munmap(mapPtr2, PAGESIZE * 2); //close(fd); return 0; } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload