【问题标题】:Virtual Memory on OSX/iOS versus Windows commit/reserve behaviourOSX/iOS 上的虚拟内存与 Windows 提交/保留行为
【发布时间】:2014-03-15 13:37:55
【问题描述】:

在将 OSX/iOS 中的虚拟内存系统行为与 Windows 进行比较时,我有点困惑。 Windows VirtualAlloc() 相关函数及其与保留和实际内存提交和取消提交有关的行为非常简单。

对于没有得到很好讨论的 OSX,我一直在研究 mach_vm_allocate()、mach_vm_map() 等。例如,如果我想创建一组跨平台函数来公开 Windows 和 OSX/iOS 之间的通用虚拟内存功能,如何我会管理 OSX 上的提交/取消提交与 Windows 上的区别吗?

我不确定我是否理解您是否可以保留虚拟地址范围并将其作为单独的操作提交,就像在 Windows 上一样?据我了解, mach_vm_allocate() 类似于带有 MEM_COMMIT | 的 VirtualAlloc() MEM_RESERVE 并尝试比较哪个实际上是一个更好的设计机制,如果有任何令人困惑的话。

可能我需要更好地了解页面管理器在 OSX 中的功能。

在 Windows 上,即使您提交了一个区域,我怀疑它实际上可能不会用物理内存支持它,除非您尝试访问它,除非当前可能有充足的内存 - 并且它只是保证在修改时交换文件支持。

在 OSX 上,我不确定如何取消提交区域但仍保留地址范围?例如,这种行为在 64 位程序(我最感兴趣)中很有用,可以为具有倒带能力的竞技场/堆栈/线性分配器保留一个大的虚拟地址范围 - 这需要能够提交和取消提交区域的末端。在 Windows 中,如何产生这样的行为是显而易见的,但在 OSX 中,我不太了解如何有效地复制它。

编辑:

我刚刚发现了这个:

Reserve memory on OS X

这与我的问题有关但是 mmap() 肯定会通过等效的 mach_vm_*() 系统调用?

编辑2:

我现在发现这些是典型的:

Does mmap with MAP_NORESERVE reserve physical memory?

How can I reserve virtual memory in Linux?

但它可能仍然没有弄清楚如何以我想要的方式取消提交 - 但要在 mmap() ANON 上搜索更多内容 - 并可能看看我是否能找到 mmap() 源代码对于 OSX (?)。

(如果我能弄清楚解除提交问题,肯定有人会说使用 mmap() 因为它也可以在 linux 上工作,但我仍然很好奇它是如何通过 mach_vm_*() 调用路由的......)

EDIT3:

我发现 mremap() 和 mmap() 一起看起来很有用!显然,使用 PROT_NONE、MAP_NORESERVE 和 mmap() 看起来也很有趣。但是我仍然不确定如何取消提交区域但仍然保留地址范围,因为 mremap() 似乎无法使用 MAP_NORESERVE 来放弃交换文件支持?

EDIT4:

我发现这与取消提交有关:https://bugzilla.mozilla.org/show_bug.cgi?id=670596。其中讨论了 OSX 和 Linux 上关于 mprotect(addr, len, PROT_NONE) 和 madvise() 的行为。 ..

EDIT5:(!)

挖掘我为 madvise() 找到的 Mac 头文件:

#define MADV_WILLNEED POSIX_MADV_WILLNEED

#define MADV_DONTNEED POSIX_MADV_DONTNEED

#define MADV_FREE 5 /* 不需要的页面,丢弃内容 */

#define MADV_ZERO_WIRED_PAGES 6 /* 将条目删除前尚未取消连线的连线页面归零 */

#define MADV_FREE_REUSABLE 7 /* 页面可以重复使用(任何人)*/

#define MADV_FREE_REUSE 8 /* 调用者想要重用这些页面 */

#define MADV_CAN_REUSE 9

所以我猜 MADV_FREE_REUSE 应该是取消提交的首选用法?

EDIT6:我在 iOS/OSX 开发者论坛上提出了这个问题,与此同时,我遇到了这些问题,这些问题可能对其他想同样问题的人有所帮助:

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

还有这个:

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

似乎关键是 mmap() 和 madvise(),或者带有标志 VM_BEHAVIOR_DONTNEED 的 mach_vm_allocate() 和 mach_vm_behavior_set()。

在试验后会为他人的利益报告...

EDIT7:

mmap() 和 madvise() 目前最新的 OSX 10.9 源代码我认为:http://www.opensource.apple.com/source/xnu/xnu-2422.1.72/bsd/kern/kern_mman.c

似乎确认了mach_vm_behavior_set()

EDIT8:

据我所知,现在可以从 OSX 10.9 得知:

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

我应该使用 mach_vm_allocate() 和 vm_map_behavior_set() 与 Windows VirtualAlloc() 用语中的(暗示性)标志大致相同:

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

但我不确定这些到底是什么意思(?):

VM_BEHAVIOR_REUSABLE
VM_BEHAVIOR_REUSE
VM_BEHAVIOR_CAN_REUSE

我希望得到苹果公司对首选使用模式的确认,但我想我已经接近用上述方法回答我自己的问题了......

这是我第一次有幸挖掘一些非常干净的开源代码:-)

【问题讨论】:

    标签: c windows macos memory virtual


    【解决方案1】:

    我在 OS X 和 iOS 上使用以下方法:

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

    // 初始化并保留内存

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

    //分配内存大小

    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
    

    // 解除分配并返回系统内存大小

    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;
    

    // 取消初始化

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

    【讨论】:

      【解决方案2】:

      没有确切的类比。在 Unix 风格的操作系统中,可以访问分配的内存,这将导致它与物理内存或交换相关联。但并不总是保证分配的内存有足够的交换文件空间留给它。因此,如果系统无法交换其他内容以释放 RAM,系统可能无法关联物理内存。

      vm_allocate()mmap() 都保留一个地址范围。它们还使进程访问该范围内的地址是合法的,此时页面将在必要时映射到物理内存。但是,在 OS X 上,我认为这两个函数都不会为地址范围保留后备存储(交换)。

      如果您分配一些空间然后访问它,导致它被映射到 RAM 或交换,并且您希望将其返回到刚刚分配但清除 RAM 或交换中的支持,我相信第二次调用 @ 987654328@ 和 MAP_FIXED 会这样做。

      【讨论】:

      • MAP_FIXED 似乎说尽可能使用特定的地址范围 - 所以你的意思是 mmap() 一个已经 mmap() 的区域,但将其标志更改为 PROT_NONE/MAP_NORESERVE 以允许它被取消提交如果需要的话?你能像那样 mmap() 一个已经 mmap() 的区域吗?
      • 我不相信 OS X 支持 MAP_NORESERVE。但你可以 mmap() 并且已经 mmap()d 区域。来自man page:“如果在标志中指定了 MAP_FIXED,系统将尝试将映射放置在指定地址,可能会删除该位置已经存在的映射。” "如果指定了 MAP_FIXED,成功的 mmap 会删除分配的地址范围内的任何先前映射。"
      • 啊,好吧,在我的谷歌搜索中似乎很难知道哪些部分适用于 OSX 和 Linux,谢谢。我也刚刚找到了带有 MADV_REMOVE 和 MADV_FREE 的 madvise() 但我不确定它们是否/如何在 OSX 上工作......
      • 顺便说一下,您不妨看看the Wine project(我是该公司的开发人员)。它实际上必须在 Linux 和 OS X 上实现 VirtualAlloc(),这与使用 mmap() 和(在 OS X 上)vm_allocate() 一样。遗憾的是,它是一个庞大而复杂的代码库,尽管大部分相关部分都在 dlls/kernel32/virtual.cdlls/ntdll/virtual.clibs/wine/mmap.c 中。
      • 这是 LGPL,是的。只有当你从中复制时,你才会受到影响。阅读它以获取实现想法或了解系统 API 的行为不会对您自己编写的代码提出许可要求。
      猜你喜欢
      • 2016-03-19
      • 2011-05-13
      • 2014-04-06
      • 1970-01-01
      • 2012-05-16
      • 1970-01-01
      • 2011-12-18
      • 1970-01-01
      • 2013-02-09
      相关资源
      最近更新 更多