一、虚拟地址和物理地址
首先,计算机系统的内存被组成一个由M个连续的字节大小组成的数组。每字节都会有一个唯一的物理地址。CPU访问内存最简单的方式就是使用物理地址。如下图:
图 1 物理地址,物理寻址
而现在都是采用的都是虚拟寻址的方法。CPU生成一个虚拟地址(VA),然后MMU(Memory Management Unit 内存管理单元) 将虚拟地址翻译成实际的物理地址,然后再进行物理寻址。
图2虚拟寻址
设想以下问题:
1. 假如内存不足会怎么样?(对于一台32位系统来说,可以使用的内存有:2^32 bytes = 4GB);内存不足,超出内存空间会导致崩溃(crash)(例如32位系统,只有1GB RAM)
2. 内存碎片化:如果连续分配内存,程序可能找不到合适的空间(run out of space),即使有空间,但是不连续。
3. 多个程序指向相同的地址:不同程序间读写相同的内容,导致数据污染或崩溃
怎么解决?让每一个程序拥有自己的虚拟内存空间。把程序的内存空间映射到物理内存(物理内存不足时把暂时不用的替换到硬盘上)。
- 虚拟内存
虚拟内存是一种计算机系统的内存管理技术,它允许程序使用比实际物理内存更大的内存空间。
在虚拟内存系统中,每个进程被分配了一个连续的虚拟地址空间,它通常比实际的物理内存大小要大得多。虚拟地址空间被划分为多个固定大小的单元,称为页面(pages)或虚拟页(virtual pages)。相应地,物理内存也被划分为与虚拟页大小相匹配的页面帧(page frames)。
当程序访问虚拟内存中的某个地址时,操作系统通过地址映射机制将虚拟地址转换为物理地址。这个映射过程涉及将虚拟页从磁盘上的页面文件(page file)中加载到物理内存中的一个页面帧中。这样,虚拟内存可以提供比实际物理内存更大的可用内存空间。
虚拟内存的主要优势是它允许在内存不足的情况下运行更大的程序或处理更大的数据集。它还提供了更好的内存管理和保护机制,使不同的进程能够相互隔离和保护彼此的内存空间。
虚拟内存本身不直接占用物理空间。虚拟内存是一种内存管理技术,它通过将部分数据存储在磁盘上的页面文件中,以释放物理内存并提供更大的可用内存空间。因此,虚拟内存的数据被分为两部分:存储在物理内存中的活动数据和存储在磁盘中的交换数据。
总而言之,虚拟内存是一种将物理内存和磁盘空间结合使用的内存管理技术,它提供了比实际物理内存更大的内存空间,并通过页面映射机制实现虚拟地址到物理地址的转换。它是现代操作系统中的重要组成部分,为程序提供了更大的内存空间和更好的内存管理功能。
虚拟页可以分为三种状态:
- 未分配的:未分配的页没有任何数据。
- 缓存的:当前已在物理内存中的已分配页。
- 未缓存的:未缓存在物理内存的已分配页。
虚拟内存是怎么解决3个问题:
1.内存不足时:当向系统申请内存但内存不足时,系统会把根据置换算法把暂进不用的内存置换到硬盘里,更新映射关系到硬盘上,再更新新申请的内存映射关系,让我们产生无限内存的错觉。(交换到硬盘后会导致性能下降,硬盘读取速度比内存慢太多了)。例如下面这个例子,程序0,1,2分别加载了物理地址的0,1,2。此时程序3进来,也需要申请内存。然后就根据算法先把内存腾出来,腾出来的放到磁盘上,再把腾出来的空间给程序3。这样就可以解决内存不足的问题而不会导致崩溃。
2.内存碎片:程序通过自身的映射表可以随意找到合适的物理内存,而不一定需要连续分配。
3.程序间相同的地址:
即使程序的地址相同,但是每个程序通过自己的映射表映射到不同的物理内存而不会互相产生干扰。但是程序间相互独立一定是好的吗?并不是,因为有些内存是需要共享的。例如不同的程序会共享系统文件,系统选择框等。让程序间实现共享内存的方法是把地址指向相同的物理内存。
内存映射主要由操作系统的内存管理单元(Memory Management Unit, MMU)管理操作的,一切都是同它来掌控。
程序访问内存的步骤:地址翻译
1. 程序指定加载一个逻辑地址
2. 计算机通过映射把逻辑地址转换成物理地址
3. 如果物理地址不在内存中,操作系统会把它从硬盘中加载到内存里,更新映射关系(原来是指向disk的,现在指向内存)
4. 计算机通过物理地址把内存内容读取出来并且返回给程序
2.1 为什么需要虚拟地址
虚拟地址的引入主要是为了满足以下几个需求和提供相应的优势:
1. 内存隔离:每个进程都拥有自己独立的虚拟地址空间,使得进程之间的内存彼此隔离。这样,一个进程无法直接访问另一个进程的内存,提高了系统的安全性和稳定性。
2. 大内存支持:虚拟地址空间可以比实际的物理内存空间大得多。这使得操作系统能够支持大容量的内存需求,可以运行更大的应用程序和处理更多的数据。
3. 内存管理灵活性:虚拟地址空间的使用可以动态地进行管理。操作系统可以根据需要将虚拟地址映射到物理内存或其他存储设备上,实现内存的灵活分配和回收,包括页面置换、页面回写和内存映射文件等技术。
4. 虚拟内存映射:虚拟地址与物理地址之间的映射关系可以通过页表等数据结构来实现。这使得操作系统能够将虚拟地址转换为物理地址,从而实现对内存的透明访问。
5. 内存保护:虚拟地址空间允许操作系统对内存区域进行权限控制和访问限制。通过设置页面级别的访问权限和保护位,操作系统可以防止非法访问和保护系统关键数据和代码。
总之,虚拟地址的引入使得操作系统能够提供更大的地址空间、更好的内存隔离、灵活的内存管理和保护机制。它为多进程环境下的程序提供了一种抽象层,使得程序可以使用独立且安全的地址空间,同时有效利用系统的资源。
- 物理地址PA:物理地址是计算机内存中实际的硬件地址。它是由计算机系统中的内存管理单元(MMU)转换而来,用于直接访问主存储器中的数据。物理地址是一个唯一的标识符,用于定位内存中的特定字节或字。
- 虚拟地址VA:虚拟地址是一种抽象的地址,由进程或程序使用。它是在虚拟内存系统中使用的地址,与物理地址相对应。虚拟地址空间被划分为不同的页(或页框),并通过地址转换机制映射到物理内存中的相应页框。虚拟地址提供了一种独立于实际物理内存布局的方式来访问内存,使得每个进程都有自己独立的地址空间。
那么虚拟地址是怎么转化成物理地址的呢?这就涉及到虚拟内存,页表等等新东西,接下来看。
三、页Page/页表/页表项(PTE)
MMU是负责把虚拟地址映射为物理地址,但凡"映射"都要解决两个问题:映射的最小单位(粒度)和映射的规则。
页(Page):MMU中VA到PA映射的最小单位称为页(Page),映射的最低粒度是单个虚拟页到物理页,页大小通常是4K,即一次最少要把4K大小的VA页块整体映射到4K的PA页块(从0开始4K对齐划分页块),页内偏移不变(低12bit),用于索引4K空间的页。
页表(Page Table),就是一个个页表条目PTE(Page Table Entry)组成的数组,每一条称为一个页表条目(Page Table Entry,即PTE)。
MMU软件配置的核心是页表(Page Table),它描述MMU的映射规则,即虚拟内存哪(几)个页映射到物理内存哪(几)个页帧。整个页表保存在片外内存,MMU通过查找页表确定一个VA应该映射到什么PA,以及是否有权限映射。
映射规则如下图:
例:如VA的一页0x30004000~0x30004fff被映射到PA的一页 0x00008000~0x00008fff,当CPU执行单元访问虚拟地址0x30004008,实际访问的物理地址是0x00008008(0x30004008和0x00008008分别位于虚实两套地址空间,互不相干,不存在重叠和冲突)。以页为最小单位,就是不能把VA中某一页划分成几小块分别映射到不同PA,也不能把VA中属于不同页的碎块映射到PA某一页的不同部分,必须页对页(4K大小)整体映射。
----------------------------
Q:页表为什么保存在主存中?
A:页表保存在片外内存的主要原因是空间的限制和灵活性的需求。
1. 空间限制:页表可能非常庞大(32bit:1 billion PTE!),特别是在具有大型虚拟地址空间的系统中。将整个页表保存在处理器的寄存器或缓存中是不切实际的,因为这些存储器有限的容量无法容纳如此大的页表。片外内存(主存)提供了更大的存储容量,可以容纳大型的页表结构。
2. 灵活性:页表的大小和结构可能随着进程的需要而变化。将页表保存在片外内存中可以更容易地进行动态调整和管理。当页表需要扩展或收缩时,可以通过修改指向页表的指针或重新分配内存来实现,而不需要对处理器的硬件结构进行改动。
3. 虚拟化支持:在虚拟化环境中,多个虚拟机可能同时运行在同一台物理机上。每个虚拟机都有自己的独立虚拟地址空间和页表。通过将页表保存在片外内存中,不同的虚拟机可以独立管理和访问自己的页表,增加了虚拟化的灵活性和隔离性。
虽然将页表保存在片外内存中增加了访问开销(因为需要额外的内存访问),但这种折衷是为了满足大型地址空间和动态的内存管理需求。同时,使用高速缓存(如TLB)来缓存最近使用的页表项可以在一定程度上减少访问延迟。
----------------------------------
四、虚拟地址和页表条目的关系
在第3节中描述了通过页表条目找到物理内存中的页。那么接下来再来看看给定一个虚拟地址,怎么找到它对应的页表条目PTE。
一个n位的虚拟地址包含两部分:一个p位的虚拟页面偏移(VPO)和一个(n-p)位的虚拟页号(VPN)。MMU正是利用了VPN来选择合适的PTE。然后将PTE中PPN和VPO串联起来,就得到了实际的物理地址。
图4-1 虚拟地址和PTE和物理地址
至此,来简要理一下简要流程。
1.CPU生成一个虚拟地址,并把它传给MMU。
2.MMU生成PTE地址,请求高速内存/内存返回实际数据。
3.内存向MMU返回PTE。
4.MMU按照图4-1逻辑,构建物理地址PA,并请求内存
5.主存返回物理地址存储的数据
五、TLB (Translation Lookaside Buffers)
但如果MMU每次地址转换都到位于外部内存的页表上查找PTE,转换速度就会大大降低,内存的速度需要几百个CPU周期。为了减少这个开销,于是出现了TLB (Translation Lookaside Buffers)即转换快表,又简称快表,可以理解为MMU内部专用的存放页表的cache,保存着最近使用的PTE乃至全部页表。
MMU接收到虚拟地址后,首先在TLB中查找,如果找到该VA对应的PTE就直接转换,找不到再去外存页表查找,并置换进TLB。TLB属于片上SRAM,访问速度快,通过TLB缓存PTE可以节省MMU访问外存页表的时间,从而加速虚实地址转换。TLB和CPU cache的工作原理一样,只是TLB专用于为MMU缓存页表。
带有TLB的虚拟地址转化过程如图5-1所示。在TLB不命中的时候,从内存中返回的PTE会被缓存在TLB中,可能会覆盖一个已存在的条目。
六、多级页表
Q:为什么要采取这种多级页表的形式?
A:省内存空间。
多级页表相较于单级页表可以节省空间的主要原因是多级页表采用了分层的结构,使得整个页表不需要一次性加载到内存中(如上图中的2^36个PTE)。这样,在实际使用中,只需要加载当前进程所使用的部分页表(4*2^9个PTE)从而减少了内存的占用。
采用多级页表的主要原因有以下几点:
1. 空间效率:多级页表可以根据需要动态分配和释放内存空间。相比于单级页表,多级页表可以节省大量的内存空间。在单级页表中,每个虚拟页都需要一个对应的页表项,而多级页表可以根据需要只分配实际使用的页表项,减少了内存开销。
2. 快速访问:多级页表可以提高地址转换的速度。在大型内存空间中,使用单级页表需要遍历整个页表来进行地址映射,这会导致访问速度变慢。而多级页表通过分层的结构,可以将大型页表划分为多个小的页表,每次只需要查找相应的页表,从而加快了地址转换的速度。
3. 灵活性:多级页表提供了更大的灵活性和可扩展性。通过增加额外的级别,可以处理更大的内存空间。同时,多级页表可以根据实际需求进行调整和优化,以适应不同的应用程序和系统环境。
总的来说,采用多级页表可以提高内存管理的效率和灵活性,减少内存开销,并提高地址转换的速度。这使得操作系统能够更好地管理大型内存空间和处理复杂的内存访问需求。
七、MMU映射失败的几种情况
1.访问了受内核保护的页面,或者访问了只读的页面,此时内核会抛出段错误。
2.VP和PP没有产生映射关系,但是数据页已经被其他进程加载到内存中了,此时只需要建立VP和PP的映射关系,称为次级缺页中断。
3.VP和PP没有产生映射关系,数据页也没有被加载到内存中(在磁盘上),此时需要发生磁盘io从磁盘中加载页到内存中,还需要建立VP和PP的映射关系,称为严重缺页中断。
除了第一点,第二第三都会以内核降低自身运行速度来修复,也就是通过中断形成页表映射,然后再重新执行引起中断的命令(此时已经更新了物理内存和PTE,数据页已经在内存中并且建立映射关系了)。
如果物理页不在内存中,CPU生成一个缺页异常(page fault exception):
1. CPU跳转到系统缺页处做清除置换
2. 系统选择某一页从内存中移除并写入硬盘中(这里涉及页面转换算法,如最优算法、先进先出算法、最近最久未使用算法、时钟算法、最不常用算法)
3. 如果被选中从内存中移除的页面是脏数据,必须把它先写到硬盘上
4. 从硬盘中读取缺页异常的页面并写到内存中
5. 更新页表的映射关系
6. 系统跳转回到引起缺页异常的指令处
参考链接:
Virtual Memory: 14 Summary (youtube.com) (合集1-14)
【MMU篇】一文总结ARMv8中的MMU架构_armv8 mmu-CSDN博客
https://zhuanlan.zhihu.com/p/484183354