简体   繁体   中英

Virtual Memory on OSX/iOS versus Windows commit/reserve behaviour

I'm a little bit confused in comparing the virtual memory system behaviour in OSX/iOS to that of Windows. The Windows VirtualAlloc() related functions and their behaviour regards reserving and the actual memory commit and de-commit are fairly straight forward.

For OSX which is not well discussed I have been looking at mach_vm_allocate(), mach_vm_map() etc. For example if I wanted to create a set of cross platform functions to expose common virtual memory functionality between Windows and OSX/iOS how would I managed the difference between commit/de-commit on OSX as opposed to Windows?

As I am not sure I understand if you can reserve virtual address range and commit it as a separate action like on Windows? From my understanding mach_vm_allocate() is similar to VirtualAlloc() with MEM_COMMIT | MEM_RESERVE and also trying to compare which is actually a better designed mechanism if any is confusing.

Possibly I need better understanding of how the page managers function in OSX.

On Windows even if you commit a region I suspect it may not actually back it with physical memory until you try accessing it unless perhaps there is currently abundant memory - and that it just guarantees swap file backing when modified.

On OSX I am not sure how to de-commit regions but still keep the address range reserved? For example this sort of behaviour is useful in a 64bit programs (Which I am mostly interested in) to reserve a large virtual address range for an arena/stack/linear allocator with rewind ability - that requires being able to commit and de-commit the ends of regions. In Windows it is obvious how to produce such behaviour but in OSX I don't quite understand how to efficiently replicate it.

EDIT:

I just found this:

Reserve memory on OS X

That is relevant to my question but surely mmap() goes through the equivalent mach_vm_*() system calls?

EDIT2:

Typical I now find these:

Does mmap with MAP_NORESERVE reserve physical memory?

How can I reserve virtual memory in Linux?

But it still perhaps doesn't clear up how to de-commit the way I would like too - but off to google some more on mmap() ANON stuff - and possibly see if I can find the mmap() source code for OSX (?).

(For sure someone will say use mmap() as it works on linux too if I can figure out the de-commit issue but still I am curious how that gets routed through the mach_vm_*() calls...)

EDIT3:

I found mremap() which together with mmap() looks useful! Obviously using PROT_NONE, MAP_NORESERVE with mmap() look interesting also. But I am still unsure how you can de-commit regions but still keep the address range preserved as mremap() doesn't seem to be able to take MAP_NORESERVE to ditch the swap file backing?

EDIT4:

I found this regards de-commits: https://bugzilla.mozilla.org/show_bug.cgi?id=670596 . Which discusses the behaviour on OSX and Linux regards mprotect(addr, len, PROT_NONE) and madvise(). ..

EDIT5: (!)

Digging through my Mac header files I find for madvise():

#define MADV_WILLNEED POSIX_MADV_WILLNEED

#define MADV_DONTNEED POSIX_MADV_DONTNEED

#define MADV_FREE 5 /* pages unneeded, discard contents */

#define MADV_ZERO_WIRED_PAGES 6 /* zero the wired pages that have not been unwired before the entry is deleted */

#define MADV_FREE_REUSABLE 7 /* pages can be reused (by anyone) */

#define MADV_FREE_REUSE 8 /* caller wants to reuse those pages */

#define MADV_CAN_REUSE 9

So I am guessing MADV_FREE_REUSE should be preferred usage for de-commits?

EDIT6: I have asked the question on the iOS/OSX developer forums and in the meantime I have come across these that may be helpful to other people wondering the same thing:

http://lists.apple.com/archives/PerfOptimization-dev/2009/Apr/msg00024.html http://markmail.org/message/yqwqd3zuawz6v5dd

Also this:

http://fxr.watson.org/fxr/source/bsd/kern/kern_mman.c?v=xnu-1228;im=bigexcerpts#L824

Which seems like the key is mmap(), and madvise(), or mach_vm_allocate() and mach_vm_behavior_set() with flag VM_BEHAVIOR_DONTNEED.

Will report back for the benefit of others after experimenting with this...

EDIT7:

Latest for now OSX 10.9 source code for mmap() and madvise() I think: http://www.opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/kern/kern_mman.c

Seems to confirm mach_vm_behavior_set()

EDIT8:

Ok now so far as I can tell from OSX 10.9:

http://www.opensource.apple.com/source/xnu/xnu-2422.1.72/osfmk/vm/vm_map.c

I should use mach_vm_allocate() and vm_map_behavior_set() with the (suggestive) flags being roughly equivalent in Windows VirtualAlloc() parlance too:

VM_BEHAVIOR_WILLNEED => Commit address range
VM_BEHAVIOR_DONTNEED => Decommit address range
VM_BEHAVIOR_FREE => Decommit and completely release address range(?)

But I am not sure what these mean exactly still(?):

VM_BEHAVIOR_REUSABLE
VM_BEHAVIOR_REUSE
VM_BEHAVIOR_CAN_REUSE

I am hoping from confirmation from apple on the preferred usage pattern but I guess I am close to answering my own question with the above...

This is the first time I have had the pleasure to dig through some very clean open source code :-)

There's no exact analog. In Unix-style OSes, allocated memory can be accessed and that will cause it to be associated with physical memory or swap. But there's not always a guarantee that allocated memory has sufficient swap file space set aside for it. So, the system can fail to associate physical memory if it can't swap something else out to free up the RAM.

Both vm_allocate() and mmap() reserve an address range. They also both make it legal for the process to access the addresses in that range, at which point the pages will be mapped to physical memory if necessary. However, on OS X, I don't believe that either function reserves backing storage (swap) for the address range.

If you allocate some space and then access it, causing it to be mapped to RAM or swap, and you want to return it to just being allocated but clear the backing in RAM or swap, I believe that a second call to mmap() with MAP_FIXED will do that.

I use method below on OS X and iOS:

char* m_base = 0;
unsigned m_offset = 0;
unsigned MAX_SIZE = 1024 * 1024 * 10; // 10 Mb

// init and reserve memory

kern_return_t err = vm_allocate(mach_task_self(), (vm_address_t*)&m_base, MAX_SIZE, VM_FLAGS_ANYWHERE);

// alloc memory size

size = (size + roundToPageSize - 1) & ~(roundToPageSize - 1);
char* address = m_base + m_offset;
kern_return_t err = vm_allocate(mach_task_self(), (vm_address_t*)&address, size, VM_FLAGS_FIXED|VM_FLAGS_OVERWRITE);
m_offset += size;
// now address points to allocated memory in reserved space

// dealloc and return to system memory size

size = (size + roundTo - 1) & ~(roundTo - 1);
char* address = m_base + m_offset - size;
kern_return_t err = vm_deallocate(mach_task_self(), (vm_address_t)address, size);
m_offset -= size;

// deinit

kern_return_t err = vm_deallocate(mach_task_self(), (vm_address_t)m_base, MAX_SIZE);
m_base = 0;
m_offset = 0;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM