蔚淦丞
发布于 2026-09-13 / 11 阅读
0
0

malloc底层两种堆内存申请方式 brk + mmap 匿名映射

前置

进程堆区位于 BSS 段之后,自低地址向高地址增长。 内核通过 mm_struct 中的 program break 标记堆的上边界。 用户态动态内存扩容,内核只提供两种底层方式:brk 主堆扩容、mmap 匿名映射扩容

1. brk /sbrk(小块内存)

函数原型与返回值

// brk 系统调用
int brk(void *addr);
  • 参数 addr:想要设置的 program break 目标虚拟地址。

  • 返回值:成功返回 0;失败返回 -1。

作用:直接把堆边界设置到指定绝对地址。

// sbrk库函数,封装brk
void *sbrk(intptr_t increment);
  • 参数 increment:地址增量。正数扩大堆;负数缩小堆;传 0 获取当前堆边界。

  • 返回值:成功返回扩容之前旧的 program break 地址;失败返回 (void*)-1

题外话:函数VS系统调用

普通函数

系统调用

运行态

用户态

用户态→内核态→用户态

指令

call,函数跳转

syscall /int 0x80,陷入内核

权限

用户态权限

内核最高权限

开销

高(上下文切换)

内存访问

只能访问用户虚拟地址空间

可访问内核 + 用户空间

作用

完成业务逻辑

请求内核操作资源

机制

  • 扩容:抬高 program break,仅拓宽虚拟地址空间,无物理内存分配;首次访问缺页、分配物理页。

  • 释放:free 只标记内存空闲,默认不回缩堆、不归还内核,仅堆顶超大空闲时才回缩边界。

特点

  • 虚拟内存连续一整块

  • 容易产生内存碎片

  • 系统调用少、速度快

  • 适用:小于 128KB 小块内存

2. mmap 匿名映射(大块内存)

函数原型与返回值

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • 参数

    • addr:期望映射起始地址,传 NULL 由内核自动分配

    • length:映射字节长度

    • prot:内存权限(可读、可写等)

    • flags:映射类型,匿名映射使用 MAP_ANONYMOUS | MAP_PRIVATE

    • fd:匿名映射填 - 1

    • offset:匿名映射填 0

  • 返回值:成功返回映射区域起始虚拟地址;失败返回 MAP_FAILED(宏,本质是(void*)-1

配套释放接口

int munmap(void *addr, size_t length);
  • 参数:映射起始地址、长度

  • 返回值:成功返回 0,失败返回 - 1

机制

  • 分配:新建独立虚拟内存,访问缺页才分配物理页

  • 释放:munmap 直接解除映射、立刻归还内核

特点

  • 每块内存离散、独立

  • 无内存碎片

  • 每次分配释放都触发系统调用,开销更高

  • 适用:大于等于 128KB 大块内存

3. 核心对比

特性

brk 主堆

mmap 匿名映射

适用场景

小块内存

大块内存

虚拟空间

连续整块

离散独立

释放逻辑

不主动归还内核

直接归还内核

碎片

有碎片

无碎片

调用开销

调用形式

brk:绝对地址;sbrk:相对增量

mmap 创建 VMA,munmap 释放

4. 终极总结

  1. brk 接收绝对地址设置堆边界;sbrk 传入增量移动边界,返回旧断点地址。mmap 创建独立匿名虚拟区域,munmap 释放。

  2. brk 靠移动堆边界扩容,连续高效但不主动释放,有碎片。

  3. mmap 新建独立虚拟内存,释放彻底无碎片,但开销更大。


评论