Win32 Programming Lesson 18: More Memory Mapped Files and the HEAP (Finally, cool stuff!)
Where are we? We’ve gotten pretty familiar with the idea of memory-mapped files but there are some important concepts we haven’t looked at Finish up today, and then look at the heap
Sharing Data There are lots of different ways to share data between processes But the “lowest level” way is really in memory, via a memory-mapped file Two or more processes map the same base address – hence they are sharing the same physical memory
Avoiding the “real” File system Very inconvenient if every memory-mapped file actually had to exist on disk Imagine that you simply wanted to use the mechanism to pass data, not keep it Can call CreateFileMapping with INVALID_HANDLE_VALUE as the hFile parameter, and the memory-mapped file is backed by the page file
30 Second Quiz What’s wrong with this code? HANDLE hFile = CreateFile(...); HANDLE hMap = CreateFileMapping(hFile,...); if (hMap == NULL) return(GetLastError());
Answer… You’ll get back INVALID_HANDLE_VALUE from the first call Which means…
MMFExample Simple program When it maps the view of the file, it transfers data between the two programs Nice method of sharing between two processes!
Sparsely Committing… Remember we talked about how to commit memory for files? Same discussion for Memory-mapped files – that is, we don’t need to commit all our memory at once
Consider CELLDATA CellData[200][256]; If sizeof(CELLDATA) is 128 that’s about 6MB. Better to share as a sparsely-committed file mapping object If we’re sharing via the paging file, can use SEC_RESERVE or SEC_COMMIT
SEC_RESERVE When you pass in SEC_RESERVE you don’t actually commit the space Just returns a HANDLE to the file mapping object Any attempts to access the memory cause a memory violation
VirtualAlloc (again) Solution: Call VirtualAlloc to allocate the memory as we use it!
The HEAP Heap is a fantastic tool for allocating small blocks of memory Perfect for linked lists and trees Advantage: can ignore allocation granularity Disadvantage: slow, with no direct control of physical allocation Better yet, internals not entirely documented
Default Each process gets a default heap of 1MB Can specify at link time (/HEAP:) Used by many Windows/C RTL functions Access to the HEAP is serialized (why, and what does this mean?) Can obtain a handle via: HANDLE GetProcessHeap();
More than 1 Heap is a… Five reasons you might want to do this: Component protection More efficient memory management Local access Avoiding thread sync overhead Quick free
1: Component Protection Imagine you have two structures: a linked list and a binary tree If you share one heap, and one has a bug, the problem may show up in the other structure If we have different heaps, problems tend to be localized (unless you *really* mess up!)
2: Memory Management Heaps work best when all the objects in them are the same size Imagine mixing two different sizes; when you free object 1 object 2 may not be a perfect fit Better to allocate all objects of the same size in the same place
3: Local Access Swapping to disk is really expensive Best to keep things you use together close together
4: Avoiding thread-sync issues Heaps are serialized by default CPU overhead involved in keeping heap access thread safe If you tell the system a heap is single- threaded, you can lose this overhead DANGER WILL ROBINSON: YOU ARE NOW RESPONSIBLE FOR THREAD SAFETY!!!
5: Quick free Instead of freeing things up block by block you can choose to free the entire heap in one go That’s *really* quick
So… how? HANDLE HeapCreate( DWORD fdwOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize); Options: 0, HEAP_NO_SERIALIZE, HEAP_GENERATE_EXCEPTIONS Serialize turns off checking for Alloc and Free Can manage this yourself via Critical Sections if you want to
HEAP_GENERATE_EXCEPTIONS Raise an exception whenever an allocation request fails Basically, it’s just about whether you want to catch exceptions or check return values – depends on the application Oh… if dwMaximumSize is 0 the size is unlimited…
Allocating Memory from the Heap PVOID HeapAlloc( HANDLE hHeap, DWORD fdwFlags, SIZE_T dwBytes); Flags: HEAP_ZERO_MEMORY, HEAP_GENERATE_EXCEPTIONS, HEAP_NO_SERIALIZE Exceptions: STATUS_NO_MEMORY, STATUS_ACCESS_VIOLATION
Changing the size… PVOID HeapReAlloc( HANDLE hHeap, DWORD fdwFlags, PVOID pvMem, SIZE_T dwBytes); New flag: HEAP_REALLOC_IN_PLACE_ONLY Means that the location won’t change
Obtaining the Size SIZE_T HeapSize( HANDLE hHeap, DWORD fdwFlags, LPCVOID pvMem); Flags: 0 or HEAP_NO_SERIALIZE
Freeing a block BOOL HeapFree( HANDLE hHeap, DWORD fdwFlags, PVOID pvMem); Flags? You tell me…
Destroying a Heap BOOL HeapDestroy(HANDLE hHeap); TRUE on success You can’t destroy the default heap!
Heaps with C++ Under C you would use malloc In C++ can use new/delete CSomeClass *pSomeClass = new CSomeClass; delete pSomeClass; Now the clever bit: overload new/delete…
Prototype class CSomeClass { private: static HANDLE s_hHeap; static UINT s_uNumAllocsInHeap; // Other private data and member functions public: void* operator new (size_t size); void operator delete (void* p); // Other public data and member functions };
Code… HANDLE CSomeClass::s_hHeap = NULL; UINT CSomeClass::s_uNumAllocsInHeap = 0; void* CSomeClass::operator new (size_t size) { if (s_hHeap == NULL) { // Heap does not exist; create it. s_hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0); if (s_hHeap == NULL) return(NULL); } // The heap exists for CSomeClass objects. void* p = HeapAlloc(s_hHeap, 0, size); if (p != NULL) { // Memory was allocated successfully; increment // the count of CSomeClass objects in the heap. s_uNumAllocsInHeap++; } // Return the address of the allocated CSomeClass object. return(p); }
And delete… void CSomeClass::operator delete (void* p) { if (HeapFree(s_hHeap, 0, p)) { // Object was deleted successfully. s_uNumAllocsInHeap--; } if (s_uNumAllocsInHeap == 0) { // If there are no more objects in the heap, // destroy the heap. if (HeapDestroy(s_hHeap)) { // Set the heap handle to NULL // so that the new operator // will know to create a new heap if a // new CSomeClass // object is created. s_hHeap = NULL; } } }
Misc Functions… DWORD GetProcessHeaps – returns handles to all heaps in the process BOOL HeapValidate – check that a heap is a-okay… UINT HeapCompact – coalesce free blocks HeapLock and HeapUnlock – used for thread sync HeapWalk – for debugging; lets you enumerate sections/blocks in the heap
Next Next, it gets difficult DLLs