虚拟内存

虚拟内存

虚拟内存

做单片机的时候,程序需要烧录进芯片,这样程序才能跑起来。单片机的CPU是直接操作内存的【物理地址】。

这种情况下,在内存中想同时运行两个程序是不可能的,第二个程序在相同的位置会擦掉第一个程序的所有内容,程序会崩溃。

操作系统时如何解决这个问题的?

这里关键的问题是两个程序都引用了绝对的物理地址,这个正是我们需要避免的。

我们把进程所使用的的地址隔离起来,让操作系统为每个进程分配独立的一套【虚拟地址】,至于虚拟地址最终怎么样落到物理内存里,对进程来说是透明的。

操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来。

如果程序要访问虚拟地址的时候,由操作系统进行转换成不同的物理地址,这样不同的进程运行写入不同的物理地址,就不会冲突。

  • 我们程序所使用的内存地址叫做虚拟内存地址
  • 实际存在硬件里面的空间地址叫做物理内存地址

进程持有的虚拟地址会通过 CPU 芯片中的内存管理单元(MMU)的映射关系,来转换成物理地址,然后通过物理地址访问内存。

内核通过硬盘上的存储空间来实现虚拟内存,这块区域称为 交换空间(swap space)。内核不断在交换空间和时间的物理内存之间反复交换虚拟内存中的内容。

virtual-memory

操作系统是如何管理虚拟地址和物理地址之间的关系?

主要有两种方式,分别的内存分段内存分页

内存分段

程序是由若干个逻辑分段组成的,可由代码分段、数据分段、栈段、堆段组成。 不同的段有不同的属性,所以就用分段(Segmentation)的形式把这些段分离出来。

分段的办法解决了程序不需要关心物理内存地址的问题,但是也有一些缺点:

  1. 内存碎片问题
  2. 内存交换的效率低的问题

内存分页

分段的好处是能产生连续的内存空间,但是会出现 外部内存碎片内存交换的空间太大 的问题。 要解决这些问题,就要想出能少出现内存碎片的办法。另外,当需要进行内存交换的时候,让需要交换写入或者从磁盘装载的数据少一点,这样就可以解决,也就是内存分页(Paging)。

分页是把整个虚拟和物理内存空间切成一段固定尺寸的大小,这样一个连续并且固定的内存空间,我们叫做 。在 Linux 下,每页大小为 4KB 。

虚拟地址和物理地址之间通过 页表 来映射。

页表存储在内存里,内存管理单元(MMU)就将虚拟内存地址转换成物理地址的工作。

当进程访问的虚拟地址在页表中查不到时,系统会产生一个 缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

分页是怎么样解决分段的【外部内存碎片和内存效率低】的问题?

内存分页由于内存空间都是预先划分好的,也就不会像内存分段一样产生间隙非常小的内存。采用分页,页和页之间是紧密排列的,所以不会有外部碎片。

但是,因为内存分页之间分配的最下单位是一页,所以会出现内存浪费,所以针对 内存分页机制会有 内部内存碎片 的现象。

如果内存空间不够,操作系统会把其他正在运行的进程中[最近没有被用]的内存页释放掉,也就是暂时写在硬盘上,称为 换出(Swap Out)。一旦需要的时候,再加载进来,称为 换入**(Swap In)**。所以一次性写入磁盘的只有少数页,不会花太多时间,内存交换的效率就相对比较高。

分页会产生的页表过大问题,有了多级页表,解决空间上问题,但是这会导致CPU在寻址的过程中,需要有很多层表参加,加大了时间的开销。 根据程序的局部性原理,在CPU芯片中加入了 TLB,负责缓存最近常被访问的页表项,大大提高了地址的转换速度。


最后修改 December 25, 2024: 菜单更新 (a57fa7d)