Linux 内存管理器以页为单位管理内存,但 Linux 进程的视角,是以内存区域VMA(Virtual memory Area)为单位划分自己的虚拟内存空间
BSS段和数据段都用于存放进程运行时的全局变量,分别存储未初始化和已初始化的全局变量
每次调用 mmap
进行内存映射时,内核会在映射区划分的一段区域用于所申请的虚拟内存,其本质上也是虚拟内存区域,它们和其他段没有任何区别,在内核中都是 struct vm_area_struct 结构表示的

使用 strace
工具查看任意一个进程的系统调用(例如ls命令),可以看到在 exec
替换ls可执行文件之前,Linux 会将.so库文件通过mmap
系统调用映射到虚拟内存中
进程的 VMA 在内核中有两种组织形式
- 一种是双向链表,所有 VMA 节点在双向链表中按照虚拟内存地址由高到低排列,用于高效遍历所有 VMA
- 另一种则是用红黑树进行组织,用于在进程空间中高效的查找 VMA,应用在进程使用mmap映射文件页,当进程需要读写大量的文件数据时
struct vm_area_struct {
unsigned long vm_start; /* Our start address within vm_mm. */
unsigned long vm_end; /* The first byte after our end address */
}
mmap
系统调用原型
#include <sys/mman.h>
void* mmap(void* addr, size_t length, int port, int flags, int fd, off_t offset);
- addr:希望内核可以从 addr 处开始创建 VMA 但不强求(除非flags=FIXED),只是个参考,NULL表明由系统自行决定起始位置
- length:分配多少虚拟内存
- port:该 VMA 的权限
- flags:决定映射的诸多行为
常见的flags以及它的含义,可以自由组合
- MAP_ANONYMOUS:匿名映射
置位为1,表示匿名映射,fd 和 offset 这两个参数也就没有了意义,fd 参数需要被设置为 -1
- MAP_FIXED:映射虚拟内存区域的选择
如果 addr~addr+length 这段虚拟内存地址已存在映射关系,根据flags的值会产生不同的行为
- 非MAP_FIXED,另选合适的位置
- MAP_FIXED,表明一定要在这段区域映射,系统会调用
unmap
解除已有的映射
- 根据实际映射的物理内存能否在多进程之间共享,又分为了两种内存映射方式:
MAP_SHARED
:表示共享映射,通过 mmap 映射出的这片内存区域在多进程之间是共享的,一个进程修改了共享映射的内存区域,其他进程是可以看到的,用于多进程之间的通信。MAP_PRIVATE
:表示私有映射,通过 mmap 映射出的这片内存区域是进程私有的,其他进程是看不到的。如果是私有文件映射,那么多进程针对同一映射文件的修改将不会回写到磁盘文件上
以上flags可以自由组合,几种常见的组合方式如下