虚拟内存
- 一、虚拟内存概述
- 二、内存分段
- 分段虚拟地址结构
- 分段映射过程
- 分段的不足之处
- 内存碎片
- 内存交换效率低
- 内存交换
- 效率低
- 三、内存分页
- 内存分页映射过程
- 分页是如何解决分段产生的物理碎片、内存交换效率低?
- 简单分页存在的问题
- 多级页表
- 四、TLB
- 五、段页式内存管理
- 五、Linux内存管理
一、虚拟内存概述
为了在多进程环境下,使得进程之间的内存地址不受影响,相互隔离,于是操作系统就为每个进程独立分配⼀套虚拟地址空间。
每个进程都有自己的虚拟空间,而物理内存只有⼀个,所以当启用了大量的进程,物理内存必然会很紧张,于是操作系统会通过内存交换技术,把不常使用的内存暂时存放到硬盘(换出),在需要的时候再装载回物理内存(换入)。
操作系统会提供⼀种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。
二、内存分段
程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段、堆段组成。不同的段是有不同的属性的,所以就用分段的形式把这些段分离出来。
分段虚拟地址结构
分段机制下的虚拟地址由两部分组成,段选择因子子和段内偏移量、
- 段选择因子子就保存在段寄存器里面。段选择因子子里最重要的是段号,用作段表的索引。段表里面保存的是这个段的基地址、段的界限和特权等级等。
- 虚拟地址中的段内偏移量应该位于 0 和段界限之间,如果段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址。
分段映射过程
分段机制会把程序的虚拟地址分成 4 个段, 每个段在段表中有⼀个项,在这⼀项找到段的基地址,再加上偏移量,于是就能找到物理内存中的地址。
分段的不足之处
内存碎片
内存交换效率低
内存交换
可以把音乐占用的那 256MB 内存写到硬盘上,然后再从硬盘上读回来到内存里。不过再读回的时候,我们不能装载回原来的位置,而是紧紧跟着那已经被占用了的 512MB内存后面。这样就能空缺出连续的 256MB 空间,于是新的200MB 程序就可以装载进来。
效率低
因为硬盘的访问速度要比内存慢太多了,每⼀次内存交换,我们都需要把一大段连续的内存数据写到硬盘上。
三、内存分页
分页是把整个虚拟和物理内存空间切成⼀段段固定尺寸的大小。这样⼀个连续并且尺寸固定的内存空间, 我们叫页。在 Linux 下,每一页的大小为4KB。
内存分页映射过程
- 把虚拟内存地址,切分成页号和偏移量
- 根据页号,从页表里面,查询对应的物理页号
- 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址
分页是如何解决分段产生的物理碎片、内存交换效率低?
由于内存空间都是预先划分好的,也就不会像分段会产生间隙非常小的内存,这正是分段会产⽣内存碎片的原因。而采用了分页,那么释放的内存都是以页为单位释放的,也就不会产生无法给进程使用的小内存。
如果内存空间不够,操作系统会把其他正在运行的进程中的最近没被使用的内存页面给释放掉,也就是暂时写在硬盘上,称为换出。⼀旦需要的时候,再加载进来,称为换入。所 以,⼀次性写入磁盘的也只有少数的⼀个页或者几个页,不会花太多时间,内存交换的效率就相对比较高。
简单分页存在的问题
操作系统可以运行非常多的进程,这意味着页表会越来越大。
多级页表
如果某个⼀级页表的页表项没有被用到,也就不需要创建这个页表项对应的⼆级页表了,即可以在需要时才创建⼆级页表。
对于 64 位的系统,两级分页肯定不够了,就变成了四级目录,分别是:
- 全局页目录项 PGD(Page Global Directory
- 上层页目录项 PUD(Page Upper Directory)
- 中间页目录项 PMD(Page Middle Directory)
- 页表项 PTE(Page Table Entry)
四、TLB
把最常访问的几个页表项存储到访问速度更快的硬件,于是计算机科学家们, 就在CPU芯片中,加入了⼀个专门存放程序最常访问的页表项的 Cache,这个 Cache 就是TLB(Translation Lookaside Buffer),通常称为页表缓存、转址旁路缓存、快表等。
有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的页表。 TLB 的命中率其实是很高的,因为程序最常访问的页就那么几个。
五、段页式内存管理
内存分段和内存分页并不是对立的,它们是可以组合起来在同⼀个系统中使⽤的,那么组合起来后,通常 称为段页式内存管理。
段页式内存管理实现的方式
- 先将程序划分为多个有逻辑意义的段,也就是前面提到的分段机制
- 接着再把每个段划分为多个页,也就是对分段划分出来的连续空间,再划分固定大小的页
- 这样,地址结构就由段号、段内页号和页内位移三部分组成
五、Linux内存管理
Linux 内存主要采用的是页式内存管理,但同时也不可避免地涉及了段机制
Linux 系统中的每个段都是从 0 地址开始的整个 4GB 虚拟空间(32 位环境下),也就是所有的段的起始 地址都是⼀样的。这意味着,Linux 系统中的代码,包括操作系统本身的代码和应用程序代码,所面对的地址空间都是线性地址空间(虚拟地址),这种做法相当于屏蔽了处理器中的逻辑地址概念,段只被用于访问控制和内存保护。