linux 内存管理
概述
linux内存管理是内核较为复杂的一部分,抽时间看了一下书,查阅了一些资料并整理记录一下。
详解
物理内存
linux内核将虚拟内存空间分为底部的用户空间和顶部的内核空间,对于这些内存的管理是内核中较为复杂的一部分,内核采用了段页式内存管理(这里的页其实是页表管理的机制)的机制来管理内存空间, 程序在运行的时候读取到的地址是逻辑地址,经过段页式转换将得到线性地址,接着经过页表映射机制最终才会访问到具体的物理地址,如下图所示:

说明一下:
cpu在运行的时候,我们的程序以及运行所需的数据的逻辑地址是采用segment+offset的方式表征逻辑地址的,这里的逻辑地址会经过** GDT(全局描述符)映射为线性地址。 而接下来会通过两张表:页目录表、页表**来完成物理地址的寻址。这里需要说明的是页目录表的层级并不是固定的。原因的话,我们可以试想一下 在32位的操作系统上是否可以管理超过4G的内存空间呢?答案是可以的,不过32位的操作系统最大的物理寻址空间就是4G,如果想要管理更多内存的话,我们可以设置多级 页目录表,这样就可以扩大寻址空间。不过这个过程需要借助高端内存来完成,不过当前应该是非主流的东西了(简单点来说可以将操作系统升级位64位并采用AMD64的cpu来硬升级), 因此这里也没必要深究了。(需要说明的是linux内核是采用了四级页目录表的方式来管理内存的)
好处
在上面的流程中我们看到物理内存的寻址是经过了两次的映射,对于真实的linux内核来说,映射的层级可能会更多,至于为什么需要多级页表映射呢?
- 对于早期的操作系统以及cpu芯片来说,都是32位的寻址空间,这样的机器可访问的最大的内存空间是4G,如果真实的物理内存大于4G,这样如果采用直接映射的方式就没有办法 访问剩余的用户空间了,这时候如果我们在用户空间的特定区域存放一个二级索引(页目录表),这样就可以极大的扩展实际的内存访问空间
- 另外一个原因是安全,正如上面所说,操作系统会将内存空间划分成用户空间和内核空间,对于用户的程序来说,其实并不关心真实的物理地址,当我们的程序在运行的时候读到 某条指令的时候,如果将逻辑地址空间直接转换成物理地址空间的话,那么我们的程序就可以直接操作真实的物理空间了,这将是一个可怕的问题,因此当前内核会增加一层映射的机制, 来将转换得到的线性地址转换成真实的物理地址,从而达到隔离和控制用户进程访问内存物理空间的过程。
页表映射是进程级的,每一个进程都会有一个独立的页目录表,这样也统一了进程访问物理地址的空间,让所有的进程在他们自身看起来分配得到的都是一个连续的内存空间,哪怕 真实分配到的物理内存并不是连续的
进程虚拟内存
上面我们知道程序在运行的时候使用的是虚拟内存空间,也说明了虚拟内存空间的必要性和好处。接下来我们来看一下进程虚拟内存空间的管理和使用,先来一睹为快,我本地 启动一个centos的docker进程来观察一下终端对应的进程的内存的划分情况,我们知道linux操作系统中一切皆文件,因此进程的虚拟内存的也是可以查看的:

上面是进程地址空间的布局,通常可以将进程的内存空间分成以下组成部分:
- 当前运行代码的二进制代码。该代码通常称之为text,所处的虚拟内存区域称之为text段
- 程序使用的动态库的代码。
- 存储全局变量和动态产生的数据的堆。
- 用于保存局部变量和实现函数/过程调用的栈。
- 环境变量和命令行参数的段。
- 将文件内容映射到虚拟地址空间中的内存映射
linux操作系统是文件系统
小结
参考视频资料:https://www.bilibili.com/video/BV1zt4y1U7rq?from=search&seid=1532338745078214906 参考书:深入linux内核架构