Linux 内存管理
为什么需要内存管理
设想一下,CPU 要是直接操作内存的物理地址,那么想要在内存中同时运行两个程序是不可能的,因为第一个程序在一个地址写入一个新值会覆盖第二个程序相同地址上的数据。
操作系统为了解决这个问题,避免了两个程序都对绝对物理地址的引用。操作系统为每个进程分配了独立一套虚拟地址,至于虚拟地址是怎么映射到物理地址,对于进程来说是透明的。
程序要访问虚拟地址时,由操作系统转换成不同的物理地址,不同进程运行时,写入的是不同的物理地址,这样就不会冲突了。
如何管理内存
针对虚拟地址空间,操作系统如何管理虚拟地址和物理地址之间的关系? 内存分段和内存分页。
内存分段:程序是由若干逻辑分段组成,可由代码分段、数据分段、栈段、堆段组成。不同的段有不同属性,所以就用分段形式把这些段分离出来。
虚拟地址通过段表与物理地址进行映射,每个段在段表中有一个项,在这一项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址。
分段机制存在内存碎片问题。解决的办法就是内存交换。内存交换的空间就是在 Linux 硬盘中划分出来的 Swap 空间。因此,内存交换是内存和硬盘数据的交换,这种方式效率太低。
内存分页:把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间叫页。在 Linux 下,每一页的大小为 4KB。虚拟地址和物理地址之间通过页表来映射。
分页机制的内存空间预先划分号,不会产生间隙非常小的内存,释放内存以页为单位释放,不会产生无法给进程使用的小内存。内存空间不够,进行内存交换的效率相对较高。
分页的方式使得在加载程序时,不需要一次性把程序加载到物理内存中,只有在程序运行时,需要用到对应虚拟内存页里面的指令和数据时,再加载到物理内存中。
内存分段和内存分页不是对立的,组合起来称为段页式内存管理:先把程序划分成多个有逻辑意义的段,再把每个段划分为多个页。
段页式地址变换得到物理地址要经过三次内存访问:第一次访问段表,得到页表起始地址,第二次访问页表,得到物理页号,第三次将物理页号和页内偏移组合,得到物理地址。
Linux 内存主要采用的是页式内存管理。Linux 系统中的每个段都是从 0 地址开始的整个 4GB 虚拟空间,这意味着,段式映射起不到作用,屏蔽了逻辑地址,Linux 中的程序包括操作系统所面对的地址空间都是虚拟地址空间。
Linux 的虚拟地址空间划分为内核空间和用户空间。用户空间从低到高分别是 7 种不同的内存段:
- 程序文件段,包括二进制可执行代码
- 已初始化数据段,包括静态常量
- 未初始化数据段,包括未初始化的静态变量
- 堆段,包括动态分配的内存,从低地址开始向上生长
- 文件映射段,包括动态库、共享内存等
- 栈段,包括局部变量和函数调用的上下文
|