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

Linux是否提供可在地址空间指定位置插入内存块的内存API及原语?

Linux下实现指定地址分配/按需扩容的底层方案

1. 直接在指定地址分配内存:mmap 搭配 MAP_FIXED

如果需要严格在指定虚拟地址分配内存块,可以使用mmap系统调用并配合MAP_FIXED标志。该标志会强制内核将内存映射到你指定的地址(前提是地址范围未被占用,且符合系统内存对齐要求)。

示例代码:

#include <sys/mman.h>
#include <unistd.h>

constexpr size_t PAGE = 64 * 1024 * 1024;

int main() {
    // 分配第一个页块
    char* p1 = (char*)mmap(NULL, PAGE, PROT_READ | PROT_WRITE,
                           MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (p1 == MAP_FAILED) { /* 错误处理 */ }

    // 在p1 + PAGE的地址分配第二个页块
    char* temp = (char*)mmap(p1 + PAGE, PAGE, PROT_READ | PROT_WRITE,
                             MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
    if (temp == MAP_FAILED) { /* 错误处理:地址被占用或不符合对齐要求 */ }

    // 使用完成后释放每个映射块
    munmap(p1, PAGE);
    munmap(temp, PAGE);
    return 0;
}
  • 注意:MAP_FIXED会覆盖指定地址范围内已有的映射(若存在),使用前务必确认地址未被占用,否则会导致数据丢失或程序崩溃。
  • 指定地址必须符合系统内存对齐要求(通常是页大小,可通过sysconf(_SC_PAGESIZE)获取)。

2. 预留大块地址空间,按需提交物理内存

这正是你提到的allocate(size, maxsize)思路的落地实现:先预留一段连续的虚拟地址空间(不立即分配物理内存),之后根据需要逐步将虚拟地址映射到物理内存,实现“按需扩容”且保持地址连续。

实现步骤:

  1. 预留虚拟地址空间:用mmap申请一段大的虚拟地址范围,设置MAP_NORESERVE(可选,避免预先占用交换空间),权限设为PROT_NONE(初始不可访问)。
  2. 按需提交物理内存:扩容时,用mprotect将部分虚拟地址的权限改为PROT_READ | PROT_WRITE,内核会自动为这部分地址分配物理内存;也可以用mmap搭配MAP_FIXED在预留地址范围内映射物理页。

示例代码:

#include <sys/mman.h>
#include <unistd.h>

// 预留最大1GB虚拟地址空间,初始分配64MB物理内存
char* allocate(size_t initial_size, size_t max_size) {
    // 预留连续虚拟地址空间
    char* base = (char*)mmap(NULL, max_size, PROT_NONE,
                             MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
    if (base == MAP_FAILED) return NULL;

    // 提交初始大小的物理内存
    if (mprotect(base, initial_size, PROT_READ | PROT_WRITE) == -1) {
        munmap(base, max_size);
        return NULL;
    }

    return base;
}

// 扩容:将已提交内存从current_size扩展到new_size(不超过max_size)
int expand(char* base, size_t current_size, size_t new_size, size_t max_size) {
    if (new_size > max_size) return -1;
    return mprotect(base + current_size, new_size - current_size, PROT_READ | PROT_WRITE);
}

// 释放整个预留空间
void deallocate(char* base, size_t max_size) {
    munmap(base, max_size);
}
  • 优势:全程保持虚拟地址连续,无需管理多个指针,扩容仅需修改权限,内核自动处理物理内存分配。
  • 注意:MAP_NORESERVE的行为受系统配置影响(比如/proc/sys/vm/overcommit_memory设置),需要提前确认。

3. 动态调整映射大小:mremap

如果已有一个内存映射,想要直接扩容并保持地址连续,可以使用mremap系统调用。它能调整已有映射块的大小,必要时可移动映射到新地址。

示例代码:

#include <sys/mman.h>
#include <unistd.h>

constexpr size_t PAGE = 64 * 1024 * 1024;

int main() {
    char* p = (char*)mmap(NULL, PAGE, PROT_READ | PROT_WRITE,
                          MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    if (p == MAP_FAILED) { /* 错误处理 */ }

    // 扩容到2*PAGE,允许内核移动映射地址
    char* expanded = (char*)mremap(p, PAGE, 2*PAGE, MREMAP_MAYMOVE);
    if (expanded == MAP_FAILED) { /* 错误处理 */ }

    // 释放扩容后的映射块
    munmap(expanded, 2*PAGE);
    return 0;
}
  • 注意:若设置MREMAP_MAYMOVE,内核可能会将映射移动到新地址以满足扩容需求;若不设置该标志,仅当原地址后有足够空闲虚拟地址时扩容才能成功。

关键注意事项

  • 所有系统调用必须处理错误返回(比如mmap返回MAP_FAILED,mprotect/mremap返回-1),务必添加错误处理逻辑。
  • 虚拟地址必须符合系统对齐要求,通常为页大小。
  • 自定义内存分配API需要自行管理已提交内存大小、最大预留空间等元数据,避免越界访问未提交的虚拟地址(会触发段错误)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:33:24