You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

64位系统下mremap(MREMAP_NOMOVE)始终返回MFAIL且errno=12求助

问题排查与解决方案

首先,errno=12对应的是ENOMEM错误,本质原因是**MREMAP_NOMOVE标志强制要求内核在原映射地址的基础上扩展空间,但原地址后方没有足够的连续空闲虚拟地址空间**。

为什么会这样?

你用的是MAP_PRIVATE|MAP_ANONYMOUS的匿名映射,内核会在用户态地址空间里找一块空闲区域分配给你。但64位系统里的地址空间虽然大,内核分配的映射区域后面不一定连着足够的空闲空间——哪怕看起来地址还有很多剩余,内核的虚拟内存管理是按页块分配的,原映射的后续页块可能已经被其他映射占用,或者内核的空闲块不足以满足扩展需求。

而当size == new_size时,mremap不需要调整空间大小,只是做个有效性校验,所以不会触发空间不足的错误,自然返回成功。

解决办法

根据你的需求,有几个可行的方案:

  • 去掉MREMAP_NOMOVE标志(推荐):允许内核重新找一块足够大的空闲地址空间,把原映射的数据迁移过去(匿名映射的话内核会直接处理页表,不需要实际拷贝物理页),这样几乎不会出现ENOMEM错误。修改后的代码:

    #define CALL_MREMAP_NOMOVE  0
    size_t size = ...;
    void *p = mmap((void *)(0), size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    if (p == MAP_FAILED) {
        perror("mmap failed");
        exit(EXIT_FAILURE);
    }
    size_t new_size = size * 2;
    // 移除MREMAP_NOMOVE,允许内核移动映射地址
    void *new_p = mremap(p, size, new_size, 0);
    if (new_p == MAP_FAILED) {
        perror("mremap failed");
        exit(EXIT_FAILURE);
    }
    p = new_p;
    
  • 提前映射足够大的空间:如果提前知道最终需要的最大大小,一开始就映射new_size的空间,后续直接使用即可,不需要调用mremap:

    size_t final_size = ...; // 比如你要的size*2
    void *p = mmap((void *)(0), final_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    // 后续直接使用前size大小的区域,需要扩展时直接用后面的空间
    
  • 尝试MAP_GROWSDOWN标志(限特定场景):如果你的映射是类似栈的向下生长结构,可以在mmap时加上MAP_GROWSDOWN标志,这样mremap扩展时可以向低地址方向尝试,但这个标志主要用于栈,使用时需要注意权限和边界(比如不能越过栈的起始地址),通用性不如前两种方案。

内容的提问来源于stack exchange,提问作者mandesero

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 06:12:16