虚拟内存


Linux 内存管理器以页为单位管理内存,但 Linux 进程的视角,是以内存区域VMA(Virtual memory Area)为单位划分自己的虚拟内存空间

BSS段和数据段都用于存放进程运行时的全局变量,分别存储未初始化和已初始化的全局变量

每次调用 mmap进行内存映射时,内核会在映射区划分的一段区域用于所申请的虚拟内存,其本质上也是虚拟内存区域,它们和其他段没有任何区别,在内核中都是 struct vm_area_struct 结构表示的

image-20250918170503908

使用 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以及它的含义,可以自由组合

  1. MAP_ANONYMOUS:匿名映射

置位为1,表示匿名映射,fd 和 offset 这两个参数也就没有了意义,fd 参数需要被设置为 -1

  1. MAP_FIXED:映射虚拟内存区域的选择

如果 addr~addr+length 这段虚拟内存地址已存在映射关系,根据flags的值会产生不同的行为

  • 非MAP_FIXED,另选合适的位置
  • MAP_FIXED,表明一定要在这段区域映射,系统会调用 unmap 解除已有的映射
  1. 根据实际映射的物理内存能否在多进程之间共享,又分为了两种内存映射方式:
  • MAP_SHARED:表示共享映射,通过 mmap 映射出的这片内存区域在多进程之间是共享的,一个进程修改了共享映射的内存区域,其他进程是可以看到的,用于多进程之间的通信。
  • MAP_PRIVATE:表示私有映射,通过 mmap 映射出的这片内存区域是进程私有的,其他进程是看不到的。如果是私有文件映射,那么多进程针对同一映射文件的修改将不会回写到磁盘文件上

以上flags可以自由组合,几种常见的组合方式如下


文章作者: AthenaCrafter
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AthenaCrafter !
  目录