【问题标题】:How to increase the size of memory region allocated with mmap()如何增加使用 mmap() 分配的内存区域的大小
【发布时间】:2021-12-20 03:41:51
【问题描述】:

我正在使用mmap Linux 系统调用分配内存。

        mov     x5, 0                   ; offset is zero
        mov     x4, -1                  ; no file descriptor
        mov     x3, 0x22                ; MAP_PRIVATE + MAP_ANONYMOUS
        mov     x2, 3                   ; PROT_READ + PROT_WRITE
        mov     x1, 4096                ; initial region size in bytes 
        mov     x0, 0                   ; let Linux choose region address
        mov     x8, 222                 ; mmap
        svc     0

是否可以增加分配的内存区域的大小以保留其起始地址和内容?怎么做才合适?

【问题讨论】:

  • 问题是关于 linux 的,与汇编无关。

标签: linux assembly system-calls mmap arm64


【解决方案1】:

在 Linux 上,使用mremap(2) 特定于 Linux 的系统调用 无需 MREMAP_MAYMOVE 来扩展现有映射,而无需考虑将这些物理页面重新映射到足够多的不同虚拟地址的选项更大的映射空间。

如果您要扩展的页面已经存在其他映射,它将返回错误。 (与 mmap(MAP_FIXED) 不同,它会默默地替换这些映射。)

如果您使用 asm 编写,那么对非 Linux 的可移植性几乎没有关系;其他操作系统将有不同的呼叫号码,可能还有 ABI,所以只需在 asm/unistd.h 中查找 __NR_mremap,并从 sys/mman.h 获取 flags 模式。


仅使用可移植的 POSIX 调用,mmap() 带有非 NULL 提示地址 = 就在您现有映射之后,但没有 MAP_FIXED;如果页面是空闲的,它将选择该地址(正如@datenwolf 所说,与早期的映射合并到一个长范围内)。否则它会选择其他地方。 (然后你必须 munmap 那个映射最终不是你想要的。)

有一个 Linux 特定的 mmap 选项:MAP_FIXED_NOREPLACE 将返回错误,而不是映射到与提示不同的地址。早于 4.17 的内核不知道该标志,并且通常会将其视为除了 MAP_ANONYMOUS 之外没有使用其他标志,因此您应该根据提示检查返回值。

不要使用MAP_FIXED_NOREPLACE | MAP_FIXED;这将在旧内核上充当MAP_FIXED,也许也可以在知道MAP_FIXED_NOREPLACE的新内核上。

假设您知道要扩展的映射的起点以及所需的新总大小,mremap 是比mmap(MAP_FIXED_NOREPLACE) 更好的选择。至少从 Linux 2.4 开始(即几十年)就已支持它,并自动保留现有的映射标志和权限(例如 MAP_PRIVATE、PROT_READ|PROT_WRITE)

如果您只知道现有映射的结束地址,mmap(MAP_FIXED_NOREPLACE) 可能是一个不错的选择。

【讨论】:

    【解决方案2】:

    如果您的原始区域后面有空闲的虚拟地址空间,只需在原始区域后面创建一个额外的 mmap-ed 区域,使用 MAP_FIXED | MAP_FIXED_NOREPLACE 标志和相同的权限。如果两个区域的页面大小相同,则它们将合并为单个映射。

    【讨论】:

    • "如果您的原始区域后面有空闲的虚拟地址空间"。问题是如何可靠地确定是否是这种情况。
    • 最简单的方法是尝试设置 noreplace 标志(相应地编辑我的答案)。 mmap 要么全有要么全无(函数调用没有允许将映射区域的大小传回给调用者的通道)。如果您想放弃可移植性并将自己限制在 Linux 上,您可以随时解析 /proc/self/maps
    • MAP_FIXED 不会“尝试”;如果有的话,它会默默地替换现有的映射。 MAP_FIXED_NOREPLACE 可能会有所帮助,但在这种情况下,彼得的 mremap 是等效的并且可能更简单。除非你非常小心,否则解析 /proc/fixed/maps 是不恰当的;标准库函数可以在您有机会创建新映射之前分配更多内存。
    • @NateEldredge:我确实为这句话编辑了我的答案。竞争条件仅在多线程进程中是一个问题;在单线程进程中,如果在读取 /p/s/maps 之前准备好所有分配并且仅使用系统调用,则 VM 映射不会更改。
    • @datenwolf:很确定你应该只使用MAP_FIXED_NOREPLACE,而不是MAP_FIXED | MAP_FIXED_NOREPLACE。它们是单独的选项,您希望不知道MAP_FIXED_NOREPLACE 的旧内核的后备是在存在冲突时忽略提示的正常行为,而不是 MAP_FIXED。不过,MAP_FIXED_NOREPLACE 的另一种安全方式,这很好。 (我将其添加到我的答案中,我认为这是对如何使用它的正确描述。)
    猜你喜欢
    • 2020-07-12
    • 2017-07-20
    • 1970-01-01
    • 2011-10-22
    • 2016-04-06
    • 1970-01-01
    • 2010-12-30
    • 2020-09-20
    相关资源
    最近更新 更多