vm_area_struct 结构中的 vm_next , vm_prev 指针分别指向 VMA 节点所在双向链表中的后继节点和前驱节点 , 内核中的这个 VMA 双向链表是有顺序的 , 所有 VMA 节点按照低地址到高地址的增长方向排序 。
双向链表中的最后一个 VMA 节点的 vm_next 指针指向 NULL , 双向链表的头指针存储在内存描述符 struct mm_struct 结构中的 mmap 中 , 正是这个 mmap 串联起了整个虚拟内存空间中的虚拟内存区域 。
struct mm_struct {struct vm_area_struct *mmap;/* list of VMAs */}在每个虚拟内存区域 VMA 中又通过 struct vm_area_struct 中的 vm_mm 指针指向了所属的虚拟内存空间 mm_struct 。

文章插图
我们可以通过
cat /proc/pid/maps 或者 pmap pid 查看进程的虚拟内存空间布局以及其中包含的所有内存区域 。这两个命令背后的实现原理就是通过遍历内核中的这个 vm_area_struct 双向链表获取的 。内核中关于这些虚拟内存区域的操作除了遍历之外还有许多需要根据特定虚拟内存地址在虚拟内存空间中查找特定的虚拟内存区域 。
尤其在进程虚拟内存空间中包含的内存区域 VMA 比较多的情况下 , 使用红黑树查找特定虚拟内存区域的时间复杂度是 O( logN ) , 可以显著减少查找所需的时间 。
所以在内核中 , 同样的内存区域 vm_area_struct 会有两种组织形式 , 一种是双向链表用于高效的遍历 , 另一种就是红黑树用于高效的查找 。
每个 VMA 区域都是红黑树中的一个节点 , 通过 struct vm_area_struct 结构中的 vm_rb 将自己连接到红黑树中 。
而红黑树中的根节点存储在内存描述符 struct mm_struct 中的 mm_rb 中:
struct mm_struct {struct rb_root mm_rb;}
文章插图
6. 程序编译后的二进制文件如何映射到虚拟内存空间中经过前边这么多小节的内容介绍 , 现在我们已经熟悉了进程虚拟内存空间的布局 , 以及内核如何管理这些虚拟内存区域 , 并对进程的虚拟内存空间有了一个完整全面的认识 。
现在我们再来回到最初的起点 , 进程的虚拟内存空间 mm_struct 以及这些虚拟内存区域 vm_area_struct 是如何被创建并初始化的呢?

文章插图
在 《3. 进程虚拟内存空间》小节中 , 我们介绍进程的虚拟内存空间时提到 , 我们写的程序代码编译之后会生成一个 ELF 格式的二进制文件 , 这个二进制文件中包含了程序运行时所需要的元信息 , 比如程序的机器码 , 程序中的全局变量以及静态变量等 。
这个 ELF 格式的二进制文件中的布局和我们前边讲的虚拟内存空间中
经验总结扩展阅读
- 神话中高要一步一步往上爬是第几集?
- 神话一步一步爬到最高是几集?
- 总以为她会永远陪我一步一步慢慢走是什么歌
- 爱情不会一步到位而是会经过颇多挫折才圆满的星座
- Dubbo 03: 直连式 + 接口工程
- 一篇文章带你了解热门版本控制系统——Git
- 一篇文章带你了解网页框架——Vue简单入门
- 一直在等待爱却不敢主动迈出一步的星座
- 我用canvas带你看一场流星雨
- 一篇文章带你掌握MyBatis简化框架——MyBatisPlus
