Conservative variable size memory sharing
Tuesday, May 17, 2011 3:10:24 PM
Instead, the following article explains how to create a mapping with potential to share hundreds of megabytes, yet still takes only as much memory as is actually necessary, which can be as low as one page of 4 kilobytes.
The following technique exploits one aspect of the behavior of memory mapping objects. Committing a page in the reserved memory-mapped region has effect across all the processes that have the region mapped into their address space. This becomes obvious when one realizes that those really are the same pages, maybe just mapped at different addresses. So let the code (and comments) speak:
if (HANDLE hShare = CreateFileMapping (INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE | SEC_RESERVE,
0, size,
TEXT ("my cool shared memory"))) {This is fairly common way to create shared memory, with one very important change: SEC_RESERVE. When we finally map the view... like this:
if (void * pShare = MapViewOfFile (hShare, FILE_MAP_WRITE, 0,0,0)) {...we receive pointer to continuous area of "size" bytes (which may be hundreds of megabytes), but none of these pages point to any physical page yet. Attempt to access them at this point will result in crash (segmentation fault). And this actually is the intended initial state of all processes that will use this shared memory.Access synchronization and passing the length value between processes is not further elaborated. I presume that this is not necessary given the subject.
Once a process needs to share (write) some amount of data, it needs to commit the necessary pages before writing into the shared memory. This is done by following call to VirtualAlloc:
if (VirtualAlloc (pShare, length, MEM_COMMIT, PAGE_READWRITE)) {VirtualAlloc formulated exactly like this allocates just enough physical pages to accommodate length bytes and assigns them to our reserved area. It only commits pages that are not already committed, the effect is cumulative, thus calling it again with smaller length will be noop. It will return the value of pShare on success or NULL on out of memory condition.
std::memcpy (pShare, pImportantData, length);
};After memcpy finishes a memory barrier should be inserted (e.g. in form of ReleaseMutex).The data are then visible and accessible to all other instances. The previous call to commit the pages is immediately in effect across all the processes that use such share, and all of them can now access the memory (up to length bytes, of course).
Conclusion
Even though the mapping object might have reserved megabytes of continuous spaces for worst-case scenario, using this technique ensures that only actually required amount of bytes is allocated.


Unregistered user # Wednesday, September 21, 2011 11:02:39 AM
Jan RingošTringi # Wednesday, September 21, 2011 11:14:03 AM
Originally posted by anonymous:
VirtualFree API function supports MEM_DECOMMIT flag that releases the memory but keeps the pages reserved. And again you would need to synchronize that operation across processes. Anyway I found out that shrinking is actually rarely really required.
Unregistered user # Thursday, September 22, 2011 6:11:20 AM
Jan RingošTringi # Thursday, September 22, 2011 8:49:46 AM
Originally posted by anonymous:
I disagree. Sharing memory is fastest way of IPC and very safe if you implement correct synchronization. Also taking advantage of a well documented feature of Windows' VM implementation is not a trick.