文章目录
- 内存管理
- 内存管理主要做什么?
- 什么是内存碎片?
- 常见内存管理方式
- 虚拟内存
- 什么是虚拟内存?
- 虚拟内存的作用?
- 没有虚拟内存的问题
- 什么是虚拟地址和物理地址?
- 分段机制
- 分页机制
- 转址旁路缓存(TLB、快表)
- 换页机制作用?
- 什么是页缺失?
- 常见的页面置换算法
- 分页机制和分段机制的共同点和不同点
- 段页机制
- 局部性原理
内存管理
内存管理主要做什么?
- 内存的分配与回收:对进程所需的内存进行分配和释放,
malloc
函数:申请内存,free
函数:释放内存。- 地址转换:将程序中的虚拟地址转换成内存中的物理地址。
- 内存扩充:当系统没有足够的内存时,利用虚拟内存技术或自动覆盖技术,从逻辑上扩充内存。
- 内存映射:将一个文件直接映射到进程的进程空间中,这样可以通过内存指针用读写内存的办法直接存取文件内容,速度更快。
- 内存优化:通过调整内存分配策略和回收算法来优化内存使用效率。
- 内存安全:保证进程之间使用内存互不干扰,避免一些恶意程序通过修改内存来破坏系统的安全性。
- …
什么是内存碎片?
- 内部内存碎片(Internal Memory Fragmentation,简称为内存碎片):已分配并被进程使用的内存空间,但因为该内存空间的大小超过了进程需要的大小,导致一部分内存空间未被使用,成为内存碎片。
- 外部内存碎片(External Memory Fragmentation,简称为外部碎片):已被分配但未被使用的内存空间,由于空间是不连续的,所以无法再使用该内存空间。
常见内存管理方式
连续内存管理:即将系统中的内存划分为一个完整的连续地址空间,进程在执行时申请一整块连续的内存来存储程序和数据。
非连续内存管理: 允许一个程序使用的内存分布在离散或者说不相邻的内存中,相对更加灵活一些。
非连续内存管理存在三种方式:
- 段式管理:将进程的地址空间分成多个逻辑段,例如代码段、数据段和栈段等,根据不同的需求分别进行分配内存空间,并且每个段的大小可以动态变化,更加灵活。
- 页式管理:把物理内存分为连续等长的物理页,应用程序的虚拟地址空间划也被分为连续等长的虚拟页,现代操作系统广泛使用的一种内存管理方式。
- 段页式管理机制:结合了段式管理和页式管理的一种内存管理机制,把物理内存先分成若干段,每个段又继续分成若干大小相等的页。
虚拟内存
什么是虚拟内存?
虚拟内存(Virtual Memory) 是计算机系统内存管理非常重要的一个技术,本质上来说它只是逻辑存在的,是一个假想出来的内存空间,主要作用是作为进程访问主存(物理内存)的桥梁并简化内存管理。
虚拟内存的作用?
总结来说,虚拟内存主要提供了下面这些能力:
- 隔离进程:物理内存通过虚拟地址空间访问,虚拟地址空间与进程一一对应。每个进程都认为自己拥有了整个物理内存,进程之间彼此隔离,一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
- 提升物理内存利用率:有了虚拟地址空间后,操作系统只需要将进程当前正在使用的部分数据或指令加载入物理内存。
- 简化内存管理:进程都有一个一致且私有的虚拟地址空间,程序员不用和真正的物理内存打交道,而是借助虚拟地址空间访问物理内存,从而简化了内存管理。
- 多个进程共享物理内存:进程在运行过程中,会加载许多操作系统的动态库。这些库对于每个进程而言都是公用的,它们在内存中实际只会加载一份,这部分称为共享内存。
- 提高内存使用安全性:控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。
- 提供更大的可使用内存空间:可以让程序拥有超过系统物理内存大小的可用内存空间。这是因为当物理内存不够用时,可以利用磁盘充当,将物理内存页(通常大小为 4 KB)保存到磁盘文件(会影响读写速度),数据或代码页会根据需要在物理内存与磁盘之间移动。
没有虚拟内存的问题
- 用户可以访问任意物理内存,可以会不小心操作到系统运行必须的内存,进而造成操作系统崩溃,严重影响了系统的安全。
- 同时运行多个程序容易崩溃,同时运行两个程序当给相同的内存地址赋值会造成覆盖,使得另一个程序崩溃。
- 程序运行中使用的所有数据或指令都要载入物理地址,根据局部性原理,其中很大一部分可能不会用到,浪费了内存资源。
- …
什么是虚拟地址和物理地址?
物理地址(Physical Address) 是真正的物理内存中地址,更具体点来说是内存地址寄存器中的地址。程序中访问的内存地址不是物理地址,而是 虚拟地址(Virtual Address) 。也就是说,我们编程开发的时候就是在和虚拟地址打交道。
操作系统一般通过 CPU 芯片中的一个重要组件 MMU(Memory Management Unit,内存管理单元) 地址翻译/地址转换(Address Translation)。通过 MMU 将虚拟地址转换为物理地址后,再通过总线传到物理内存设备,进而完成相应的物理内存读写请求
MMU 将虚拟地址翻译为物理地址的主要机制有3种: 分段机制、分页机制和段页机制 。
现代操作系统广泛采用分页机制,重点关注!
虚拟地址空间和物理地址空间:
- 虚拟地址空间是虚拟地址的集合,是虚拟内存的范围。每一个进程都有一个一致且私有的虚拟地址空间。
- 物理地址空间是物理地址的集合,是物理内存的范围。
分段机制
分段机制是一种计算机内存管理技术,将进程的逻辑地址空间划分为多个逻辑段,每个逻辑段都是一个连续的地址区间,它们分别包含不同类型的数据和代码,例如代码段、数据段和栈段等。
在分段机制中,每个逻辑段都可以根据需要动态调整其大小,从而更好地适应进程的需求。与内存分页机制相比,分段机制更加灵活,能够更好地适应不同的进程需求。
但分段机制也存在一些缺点,例如逻辑地址的转换开销大,需要额外的硬件支持,同时也容易发生外部碎片等问题。
分页机制
分页机制(Paging) 把主存(物理内存)分为连续等长的物理页,应用程序的虚拟地址空间划也被分为连续等长的虚拟页。现代操作系统广泛采用分页机制。
注意:这里的页是连续等长的,不同于分段机制下不同长度的段。
在分页机制下,应用程序虚拟地址空间中的任意虚拟页可以被映射到物理内存中的任意物理页上,因此可以实现物理内存资源的离散分配。分页机制按照固定页大小分配物理内存,使得物理内存资源易于管理,可有效避免分段机制中外部内存碎片的问题。
在分页机制下,每个应用程序都会有一个对应的页表。
分页机制下的虚拟地址由两部分组成:
- 页号:通过虚拟页号可以从页表中取出对应的物理页号;
- 页内偏移量:物理页起始地址+页内偏移量=物理内存地址。
具体的地址翻译过程如下:
- MMU 首先解析得到虚拟地址中的虚拟页号;
- 通过虚拟页号去该应用程序的页表中取出对应的物理页号(找到对应的页表项);
- 用该物理页号对应的物理页起始地址(物理地址)加上虚拟地址中的页内偏移量得到最终的物理地址。
页表中还存有诸如访问标志(标识该页面有没有被访问过)、页类型(该段的类型,例如代码段、数据段等)等信息。
通过虚拟页号一定要找到对应的物理页号吗?找到了物理页号得到最终的物理地址后对应的物理页一定存在吗?
不一定,可能会存在页缺失,为了解决这个问题,操作系统引入了 多级页表。多级页表属于时间换空间的典型场景,利用增加页表查询的次数减少页表占用的空间。
转址旁路缓存(TLB、快表)
为了提高虚拟地址到物理地址的转换速度,操作系统在 页表方案 基础之上引入了 转址旁路缓存(Translation Lookasjde Buffer,TLB,也被称为快表) 。
TLB属于内部的单元,本质上就是一块高速缓存,缓存了虚拟页号到物理页号的映射关系,可以将其简单看作是存储着键(虚拟页号)值(物理页号)对的哈希表。
使用 TLB 之后的地址翻译流程是这样的:
- 用虚拟地址中的虚拟页号作为 key 去 TLB 中查询;
- 如果能查到对应的物理页的话,就不用再查询页表了,这种情况称为 TLB 命中(TLB hit)。
- 如果不能查到对应的物理页的话,还是需要去查询主存中的页表,同时将页表中的该映射表项添加到 TLB 中,这种情况称为 TLB 未命中(TLB miss)。
- 当 TLB 填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。
由于页表也在主存中,因此在没有 TLB 之前,每次读写内存数据时 CPU 要访问两次主存。有了 TLB 之后,对于存在于 TLB 中的页表数据只需要访问一次主存即可。
TLB 的设计思想非常简单,但命中率往往非常高,效果很好。这就是因为被频繁访问的页就是其中的很小一部分。
换页机制作用?
换页机制的思想是当物理内存不够用的时候,操作系统选择将一些物理页的内容放到磁盘上去,等要用到的时候再将它们读取到物理内存中。也就是说,换页机制利用磁盘这种较低廉的存储设备扩展的物理内存。
这也就解释了一个日常使用电脑常见的问题:为什么操作系统中所有进程运行所需的物理内存即使比真实的物理内存要大一些,这些进程也是可以正常运行的,只是运行速度会变慢。
这同样是一种时间换空间的策略,你用 CPU 的计算时间,页的调入调出花费的时间,换来了一个虚拟的更大的物理内存空间来支持程序的运行。
什么是页缺失?
页缺失(Page Fault,又名硬错误、硬中断、分页错误、寻页缺失、缺页中断、页故障等)指的是当软件试图访问已映射在虚拟地址空间中,但是目前并未被加载在物理内存中的一个分页时,由 MMU 所发出的中断.
常见的页缺失有下面两种:
- 硬性页缺失(Hard Page Fault):物理内存中没有对应的物理页。于是,Page Fault Handler 会指示 CPU 从已经打开的磁盘文件中读取相应的内容到物理内存,而后交由 MMU 建立相应的虚拟页和物理页的映射关系。
- 软性页缺失(Soft Page Fault):物理内存中有对应的物理页,但虚拟页还未和物理页建立映射。于是,Page Fault Handler 会指示 MMU 建立相应的虚拟页和物理页的映射关系。
发生上面这两种缺页错误的时候,应用程序访问的是有效的物理内存,只是出现了物理页缺失或者虚拟页和物理页的映射关系未建立的问题。如果应用程序访问的是无效的物理内存的话,还会出现 无效缺页错误(Invalid Page Fault)
常见的页面置换算法
常见的页面置换算法有:先进先出页面置换算法、最佳页面置换算法、时钟页面置换算法、最少使用页面置换算法、最近最久未使用页面置换算法。
- 最佳页面置换算法(OPT,Optimal):优先选择淘汰的页面是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若干页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现,只是理论最优的页面置换算法,可以作为衡量其他置换算法优劣的标准。
- 先进先出页面置换算法(FIFO,First In First Out) : 最简单的一种页面置换算法,总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。该算法易于实现和理解,一般只需要通过一个 FIFO 队列即可需求。不过,它的性能并不是很好。
- 最近最久未使用页面置换算法(LRU ,Least Recently Used):LRU 算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 T,当须淘汰一个页面时,选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰。LRU 算法是根据各页之前的访问情况来实现,因此是易于实现的。OPT 算法是根据各页未来的访问情况来实现,因此是不可实现的。
- 最少使用页面置换算法(LFU,Least Frequently Used) : 和 LRU 算法比较像,不过该置换算法选择的是之前一段时间内使用最少的页面作为淘汰页。
- 时钟页面置换算法(Clock):可以认为是一种最近未使用算法,即逐出的页面都是最近没有使用的那个。
分页机制和分段机制的共同点和不同点
共同点:
- 都是非连续内存管理的方式。
- 都采用了地址映射的方法,将虚拟地址映射到物理地址,以实现对内存的管理和保护。
不同点:
- 分页机制以页面为单位进行内存管理,而分段机制以段为单位进行内存管理
- 也是物理单位,即操作系统将物理内存划分为固定大小的页面。而段则是逻辑单位,是为了满足程序对内存空间的逻辑需求而设计的。
- 分段机制容易出现外部内存碎片。分页机制解决了外部内存碎片的问题,但仍然可能会出现内部内存碎片
- 分页机制采用了页表来完成虚拟地址到物理地址的映射。而分段机制采用了段表来完成虚拟地址到物理地址的映射。
- 分页机制对程序没有任何要求。二分段机制需要程序员将程序分为多个段,并且显式的使用段寄存器来访问不同的段。
段页机制
结合了段式管理和页式管理的一种内存管理机制,把物理内存先分成若干段,每个段又继续分成若干大小相等的页。
在段页式机制下,地址翻译的过程分为两个步骤:
- 段式地址映射。
- 页式地址映射。
局部性原理
局部性原理是指在程序执行过程中,数据和指令的访问存在一定的空间和时间上的局部性特点。其中,时间局部性是指一个数据项或指令在一段时间内被反复使用的特点,空间局部性是指一个数据项或指令在一段时间内与其相邻的数据项或指令被反复使用的特点。
在分页机制中,页表的作用是将虚拟地址转换为物理地址,从而完成内存访问。在这个过程中,局部性原理的作用体现在两个方面:
- 时间局部性:由于程序中存在一定的循环或者重复操作,因此会反复访问同一个页或一些特定的页,这就体现了时间局部性的特点。为了利用时间局部性,分页机制中通常采用缓存机制来提高页面的命中率,即将最近访问过的一些页放入缓存中,如果下一次访问的页已经在缓存中,就不需要再次访问内存,而是直接从缓存中读取。
- 空间局部性:由于程序中数据和指令的访问通常是具有一定的空间连续性的,因此当访问某个页时,往往会顺带访问其相邻的一些页。为了利用空间局部性,分页机制中通常采用预取技术来预先将相邻的一些页读入内存缓存中,以便在未来访问时能够直接使用,从而提高访问速度。