MPU与MMU

MPU

MPU 有以下能力可以增加系统的健壮性

  • 可以阻止用户去破坏操作系统需要使用的数据
  • 可以防止一个任务去非法访问其他任务的数据,将任务完全隔离开
  • 可以把关键数据区设为只读,从而不被破坏
  • 检测其他意外访问,比如,堆栈溢出,数组越界等。

原理

MPU(Memory Protection Unit,内存保护单元),MPU是内存管理单元(MMU)的简化版本,在 Cortex-M内核中是可选模块,带MPU的微控制器允许内存映射(包括Flash、RAM和外围设备)细分为若干区域,分别给每个区域分配不同的访问权限。

MPU允许处于特权模式的程序定义内存区间,分配内存访问权限以及每个区间的属性,不同的处理器能够支持的内存分区都不一样,Cortex-M7系列可以拥有8个或16个内存分区。

配置/操作MPU时我们都是基于分区(region)设置,每一个region都可以分配不同的访问规则,存储类型以及它们的属性决定了访问这个分区的行为。

分区时常常会碰到分区地址有交叉的情况,此时分区属性将由分区号更高的设定所决定,例如,如果MPU可以设置16个分区,那么分区号为15的分区属性会是这段地址最终的属性。

MPU类型有两种:Normal和Device/Strongly-Ordered。
对于Normal类型的分区,处理器为了运行效率可能会重排事务(transaction),或者进行预测读操作。而对于Device/Strongly-Ordered类型的分区,处理器必须保持对设备或者强顺序存储按照代码顺序执行事务。当然,对于Device和Strongly-Ordered两个细分分区类型,它们对于执行顺序也有不一样的地方,外部存储可以缓存期望写入device的数据,但是不允许对写入Strong-Ordered存储数据的缓存。

MPU硬件强加了两个规则,区域起始地址和大小定义必须遵守:
1、区域大小必须是32字节到4G(包括)之间的二进制的2次方。例如,32字节、64字节、128字节、256字节等等都是有效的区域大小。
2、起始地址必须是区域大小的倍数。例如,一个配置为65536字节长的区域必须从能被65536整除的地址开始。

MPU 本质上就是为了保护某一段地址区域不被非授权状态的程序进行访问。

参考

ARM Cortex-M3 Optional Memory Protection Unit.
MPU:鸿蒙轻内核的任务栈的溢出检察官.
STM32H7理论学习:MPU,内存保护单元.
[Classic AUTOSAR学习]以S32G为例,基于Cortex-M7的内存保护单元(MPU).

MMU

我们知道应用程序不能随意访问内存的,如果让应用程序直接访问内存,计算机内存中所有的内容将被暴露出来,这是很危险的。所以出现了MMU,MMU是内存管理单元,应用程序访问的是虚拟内存,虚拟内存能够通过MMU的转换,变成物理内存。

内存管理的好处:

  • 为编程提供方便统一的内存空间抽象,在应用开发而言,好似都完全拥有各自独立的用户内存空间的访问权限,这样隐藏了底层实现细节,提供了统一可移植用户抽象。
  • 以最小的开销换取性能最大化,利用MMU管理内存肯定不如直接对内存进行访问效率高,为什么需要用这样的机制进行内存管理,是因为并发进程每个进程都拥有完整且相互独立的内存空间。那么实际上内存是昂贵的,即使内存成本远比从前便宜,但是应用进程对内存的寻求仍然无法在实际硬件中,设计足够大的内存实现直接访问,即使能满足,CPU利用地址总线直接寻址空间也是有限的。

从操作系统角度来看,虚拟内存的基本抽象由操作系统实现完成:

  • 处理器内存空间不必与真实的所连接的物理内存空间一致。
  • 当应用程序请求访问内存时,操作系统将虚拟内存地址翻译成物理内存地址,然后完成访问。

虚拟内存系统会将虚拟内存,划分为固定大小的块(又叫做最小粒度,一般有4KB、16KB、64KB等大小,其中4KB最流行),这个块我们称作为虚拟页(Virtual Page简称VP),同理将物理内存划分为物理页(Physical Page简称PP),也叫页帧(Page Frame)

每个虚拟页的首地址,会被维护在一个表内,这个表叫做查询表或页表(Page Table,简称PT),页表内的每一个条目,被称为页表项(Page Table Entry,简称PTE)

这个页表可能有多级,一般来说,级数越多,能覆盖的虚拟地址范围就越大。在多级页表当中,除最后一级页表之外的所有页表,它的页表项存放的不一定是物理页,也可能是下一级页表的地址。
在这里插入图片描述

在这个基础上,虚拟系统会产生一个虚拟地址,由虚页号+页偏移组成。 虚页号存放的一般是页表项的偏移地址(也可能是多级页表的组合),通过对页表的不断查询,最后找到对应的物理页(的首地址),然后加上虚拟地址的页偏移,就能顺利计算出真实的物理地址。
在这里插入图片描述

虚拟存储器(Virtual Memory)

虚拟存储存的基本思想是:

程序、数据、堆栈的总的大小可以超过物理存储器的大小,操作系统把当前使用的部分保留在内存中,而把其他未被使用的部分保存在磁盘上。

与虚拟地址空间 和 虚拟地址相对应的则是物理地址空间 和 物理地址,多数时候系统所具备的物理地址空间只是虚拟地址空间的一个子集。

这时举一个最简单的例子直观的说明这两者,对于一台内存为256MB 的32Bit x86 主机来说,它的虚拟地址空间范围是0x0 ~ 0xFFFF FFFF (4G),而物理地址空间范围是0x0000 0000 ~ 0x0FFF FFFF ( 256MB )。

在没有使用虚拟地址的机器上,虚拟地址被直接送到内存总线上,使具有相同地址的物理存储被读写。而使用了虚拟存储的情况下,虚拟地址不是被直接送到内存地址总线上,而是送到内存管理单元— MMU。MMU由一个或一组芯片组成,一般存在于协处理器中,其功能是把虚拟地址映射为物理地址。

在这里插入图片描述

  1. CPU 看到的是 Virtual Adress (程序中的逻辑地址)
  2. Caches 和 MMU 使用的是 MVA (实际的虚拟地址 MVA = (pid << 25) | VA)
  3. 实际物理设备使用的是 Physical Address (物理地址)

MMU的工作过程

大多数使用虚拟存储器的系统都使用一种称为分页(paging)。

虚拟地址空间划分为页(page)的单位,而相应的物理地址空间也被进行划分,单位是页框(frame)。页和页框的大小必须相同。

接下来配全图片,以一个例子说明页与页框之间在MMU 的调度下是如何进行映射的:

在这里插入图片描述

在这个例子中,我们有一个可以生成16位地址的机器,它的虚拟地址范围从0x0000 ~ 0xFFFF(64k),而这台机器只有32K 的物理地址,因此它可以运行64K 的程序,但该程序不能一次性调入内存运行。

这台机器必须有一个达到可以存放64K 程序 的外部存储器(例如磁盘或Flash) 以保证程序片段在需要时可以被调用。

这个例子中,页的大小为64K ,页框大小与页相同(这点必须保证的,内存和外围存储器之间传输总是以页为单位),对 应64K 的虚拟地址和32K 的物理存储器,它们分别包含了16 个页 和 8 个页框。

执行下面这些指令:

MOVE REG,0 // 将 0 号地址的值传递进寄存器 REG

虚拟地址 0 将被送往MMU,MMU看到该虚拟地址落在页0 范围内(页0 范围是0 到 4095),从上图我们可以看出页0 所对应的(映射)的页框为 2(页框2的地址范围是8192 到 12287)。

因此,MMU 将该虚拟地址转化为物理地址 8192, 并把地址8192送到地址总结上。

内存对MMU 的映射一无所知,它只看到一个对地址8192的读请求并执行它,MMU 从而将8192 到 12287换虚拟地址解析为对应的物理地址 0 到 4096 。

MOVE REG , 20500 //被转换为----> MOVE REG, 12308

因为虚拟地址20500 在虚页5(虚拟地址范围是20480 到 24575)距开头20个字节处,虚页5映射到页框3(页框3的地址范围是12288 到 16383),于是被映射到物理地址12288 + 20 = 12308。

MOV REG , 32780

虚拟地址32780 落在页 8 的范围内,从上图我们看出,页8并没有被 有效的进行映射(该页被打上X),这时又会发生什么呢?

MMU 注意到这个页没有被映射,于是通知CPU 发生一个缺页故障(page fault),这种情况下,操作系统必须处理这个页故障,它必须从8个物理页框中找到一个很少被使用的页框,并把该页框的内容写入外围存储器(这个动作被称为page copy),随后把需要引用的页(本例 是页8)映射到刚才被释放的页框中(这个动作被称为修改映射关系),然后重新执行产生故障的指令(MOV REG, 32780).

假定操作系统,决定释放页框1, 以使以后任何对虚拟地址4K 到 8K 的访问都引起故障而使操作系统做出适当的动作。

其次它把虚页8 对应的页框号由X 变为1, 因此得新执行MOV REG, 32780,MMU 将32780 映射为 4180。

我们已经知道,大多数使用虚拟存储器的系统都使用一种称为分页(paging)的技术,就象我们刚才所举的例子,虚拟地址空间被分为大小相同的一组页,每个页有一个用来标示它的页号(这个页号一般是它在该组中的索引,这点和C/C++中的数组相似)。

在上面的例子中04K的页号为0,48K的页号为1,8~12K的页号为2,以此类推。

而虚拟地址(注意:是一个确定的地址,不是一个空间)被MMU分为2个部分,第一部分是页号索引(page Index),第二部分则是相对该页首地址的偏移量(offset).

我们还是以刚才那个16位机器结合下图进行一个实例说明,该实例中,虚拟地址8196被送进MMU,MMU把它映射成物理地址。16位的CPU总共能产生的地址范围是0~64K,按每页4K的大小计算,该空间必须被分成16个页。而我们的虚拟地址第一部分所能够表达的范围也必须等于16(这样才能索引到该页组中的每一个页),也就是说这个部分至少需要4个bit。
在这里插入图片描述

该地址的页号索引为0010(二进制码),即索引的页为页2,第二部分为000000000100(二进制),偏移量为4。

页2中的页框号为6(页2映射在页框6,见上图),我们看到页框6的物理地址是24~28K。于是MMU计算出虚拟地址8196应该被映射成物理地址24580(页框首地址+偏移量=24576+4=24580)。

同样的,若我们对虚拟地址1026进行读取,1026的二进制码为0000010000000010,page index=“0000”=0,offset=010000000010=1026。

页号为0,该页映射的页框号为2,页框2的物理地址范围是8192~12287,故MMU将虚拟地址1026映射为物理地址9218(页框首地址+偏移量=8192+1026=9218)。

虚拟内存管理

现代操作系统普遍采用虚拟内存管理(Virtual Memory Management)机制,这需要处理器中的MMU(Memory Mangement Unit,内存管理单元)提供支持。

首先引入两个概念,虚拟地址和物理地址。

如果处理器没有MMU,或者有MMU 但没有启用,CPU执行单元发出的内存地址将直接传到芯片引脚上,被物理内存芯片接收,这称为物理地址。
如果处理器启用了MMU,CPU执行单元发出的内存地址将被 MMU 截获,从CPU到MMU 的地址称为虚拟地址,而MMU 将这个地址翻译成另一个地址,发到CPU芯片的外部地址引脚上,也就是将VA映射成了PA 了。
如果是32位处理器, 则内存地址总线是32位的,与CPU 执行单元相连,而经过MMU转换后的外地址总线则不一定是32位。

也就是说,虚拟地址空间与物理地址空间是独立的,32位处理器的虚拟地址空间是4GB,而物理地址空间既可以大于也可以小于4G。

MMU 将 VA映射到PA是以页(page)为单位的,32位处理器的页尺寸通常是4KB。

例如:
MMU 可以通过一个映射项将VA 的一页0xB7001000 - 0xB7001FFFF映射到PA 的一页0x2000 ~ 0x2FFF。

如果CPU 执行单元要访问虚拟地址0xB7001008,则实际访问到的物理地址是0x2008。

物理内存中的页称为物理页帧(page frame),虚拟内存的哪个页面映射到物理内存的哪个页帧是通过页表(Page Table)来描述的,页表保存在物理内存中,MMU 会查找页表来确定一个VA 应该映射到什么PA.

操作系统和 MMU 是这样配合的:操作系统在初始化或分配、释放内存时会执行一些指令在物理内存中填写页表,然后用指令设置MMU,告诉MMU 页表在物理内存中的什么位置。

设置好之后,CPU 每次执行访问内存的指令都会自动引发MMU 做查表和地址转换操作,地址转换操作由硬件自动完成,不需要用指令控制MMU 去做。

我们在程序中使用的变量和函数都有各自的地址,程序被编译后,这些地址就成了指令中的地址,指令中的地址被 CPU解释执行,就成了CPU的执行单元发出的内存地址,所以在启用MMU 的情况下,程序中使用的地址都是虚拟地址,都会引发MMU 做查表和地址转主换操作。

那为什么要设计这么复杂的内存管理机制呢? 多了一层VA 到 PA 的转换到底换来什么好处?

MMU 除了做地址转换之外,还提供内存保护机制,各种体系结构都有用户模式(User Mode)和特权模式(Privileged Mode)之分,操作系统可以在页表中设置每个内存页面的访问权限

  • 有些页面不允许访问,

  • 有些页面只有在CPU 处于特权模式时才允许访问,

  • 有些页面在用户模工和特权模式都可以访问,访问权限又分为可读、可写 和可执行三种。

这样设定好之后,当CPU 要访问一个VA 时,MMU都会检查CPU 当前处于用户模式还是特权模式,访问内存的目的是读数据、写数据、还是取指令,如果和操作系统设定的页面权限相符,就允许访问,把它转换成PA ;如果不允许访问,就产生一个异常(Exception)。

异常处理过程和中断类似,不同的是中断由外部设备产生而异常由CPU 内部产生,中断产生的原因和CPU 当前执行的指令无关,而异常的产生就是由于CPU 当前执行的指令出了问题,例如,访问内存的指令被 MMU 检查出权限错误,除法指令的除数为0 都会产生异常。

用户空间和内核空间

通常操作系统把虚拟地址划分为用户空间和内核空间,例如 X86平台的Linux 系统虚拟地址空间是0x00000000 - 0xFFFFFFFF,前3GB(0x00000000 - 0xBFFFFFFF)是用户空间,后1GB(0xC0000000 - 0xFFFFFFFF)是内核空间。

用户程序加载到用户空间,在用户模式下执行,不能访问内核中的数据,也不能跳转到内核代码中执行。

这样可以保护内核,如果一个进程访问了非法地址,顶多这一个进程崩溃,而不会影响到内核和整个系统的稳定性。

CPU 在产生中断和异常时不仅会跳转到中断或异常服务程序,还会自动切换模式,从用户模式切换到特权模式,因此从中断或异常服务程序可以跳转到内核代码中执行。

事实上,整 个内核就是由各种中断和异常处理程序组成的。

总结下:
在正常情况下 ,处理器在用户模式执行用户程序,在中断或异常情况下处理器切换到特权模式执行内核程序,处理完中断或异常之后再返回用户模式继续执行用户程序。

段错误

段错误是这样产生的:用户程序要访问一个VA, 经MMU 检查无权访问,MMU 产生一个异常,CPU 从用户模式切换到特权模式,跳转到内核代码中执行异常服务程序。内核把这个异常解释为段错误,把相发异常的进程终止掉。

物理地址、虚拟地址、逻辑地址

  • 物理地址:物理存储器是由处理器外部地址总线寻址的存储器,物理存储器按8位的字节序列加以组织,每个字节有唯一的地址与之对应。

    在CPU实模式下,没有分段或分页机制,CPU不进行自动地址转换,这时程序操作的就是物理地址。

  • 逻辑地址:操作系统或应用程序面对的存储单元地址的表示形式。分段存储管理方式把内存划分为多个逻辑段(代码段、数据段、堆栈段等),这种情况下,用 “段起始地址+段内偏移地址” 这种形式来描述数据地址就是很自然的,这就是所谓的逻辑地址,它的描述形式是段号:偏移地址。

    • 逻辑地址是面向上层程序员的。
    • 逻辑地址并不一定是元素存储的真实地址,即数组元素的物理地址(在内存条中所处的位置),可能并非是连续的,只是操作系统通过地址映射,将逻辑地址映射成连续的,这样更符合人们的直观思维。
    • 逻辑地址只是一个描述形式,cpu真正用来寻址的是虚拟地址,而虚拟地址是用逻辑地址形式描述的。
  • 虚拟地址:用于指示虚拟存储器的地址,它是用逻辑地址指示的。

    • 只有保护方式MMU才支持虚拟地址,实方式是不支持的。
    • 在保护方式下的逻辑地址用段选择子指示段号。
  • 几种地址之间的关系图在这里插入图片描述
    参考:我理解的物理地址、虚拟地址、逻辑地址;

参考:

Linux内核 MMU的工作原理

  • 2
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值