当前常见的解决方案是使用 MMU,即内存管理单元。无需只考虑 Intel 或 arm。
您可以查找术语虚拟内存和物理内存,尽管使用术语虚拟内存存在问题。
物理内存是处理器的地址空间,从 0x000...0000 到 0xFFF...FFF,但地址有很多位。
虚拟内存不需要单独的处理器模式,但在一般实现中需要,这允许在内核(如果您愿意的话,操作系统)和应用程序之间进行隔离。在处理器和 mmu 之间的核心地址总线上,会显示 id 以及地址和数据。操作系统设置 mmu 表,这些表定义了一块虚拟内存并描述了物理地址。因此,特定应用程序的 0x00000000 处的 16K 字节的虚拟地址块可能会映射到物理内存中的 0x12300000。对于同一个应用程序,0x00004000 可能映射到 0x32100000 等等,这使得操作系统的内存分配更容易,如果你想分配一兆字节的内存,它不必找到线性/对齐的空闲内存块,但可以用较小的未分配/空闲内存块构建它。这使得应用程序可以认为它可以访问大部分处理器内存空间。
有不同的设计实现,但为了保护操作系统和应用程序,总线上使用的 id 区分应用程序和操作系统。如果总线事务包含 id 和 id 无权访问的地址的组合(每个块都有访问/保护位以某种形式指示 id 是否可以访问该虚拟地址),则 mmu 会生成一个错误,该错误是以处理器特定的方式对处理器的某种异常/中断,它将处理器切换到受保护/内核模式并触发中断/异常处理程序。
这不一定是坏事。例如,当运行虚拟机而不是应用程序时,可以有意设计虚拟机软件,使特定的虚拟地址模拟一些外围设备,例如以太网控制器,以便 VM 可以访问网络。当应用程序命中解决故障时,您不是关闭应用程序并通知用户出现问题,而是基于该地址通过对应用程序无法响应的结果或将结果返回给应用程序来模拟外围设备从真正的外围设备中分辨出来。故障的另一个特征是外行(不是程序员/软件/硬件工程师)版本的虚拟内存。
这是您的应用程序可以认为它可以访问所有计算机内存的地方。应用程序可能已用完系统中的所有空闲内存 (RAM)。但是在他们的虚拟地址空间中,没有人真正做到这一点,在某个时候,应用程序可能已经将物理 0x11100000 分配给了虚拟 0x20000000,但是系统需要分配内存并且没有更多可用的内存。操作系统可以使用算法来确定此应用程序有一段时间没有使用其空间,或者更有可能是随机抽奖,并将块在 0x11100000 物理上并将其内容复制到硬盘驱动器/(非 ram 存储),标记为虚拟0x20000000 以便在访问时会出错,并为当前的内存分配请求(可能是相同的应用程序或不同的应用程序)提供物理 0x11100000。
当此应用程序出现并访问 0x20000000 处的内存块时,操作系统会出现故障,选择其他一些内存块,将其保存到磁盘,将其标记为故障,获取此应用程序 0x20000000 中的内容磁盘将它放在内存中,释放故障并且应用程序继续运行。这就是为什么当您的系统内存用完并进入“交换”内存(有时也称为虚拟内存)时性能会急剧下降的原因。
如果 mmu 存在并且处理器设计为与操作系统一起使用,那么理想情况下,有一种快速切换 mmu 表的方法。对于单线程处理器来说,为了简化这一点,一次只能运行一件事,即使用户感觉有很多事情在发生,一次只有一组指令在运行,它们要么来自特定的应用程序,要么来自特定的应用程序。操作系统中的处理程序。每个处理器 id 每个应用程序和内核本身都需要一个 mmu 表(您通常不会关闭 mmu,您只需让内核完全访问内存空间,或者 mmu 知道未检查特定 id,特定于设计mmu/系统)。 mmu 表存在于内存中,但 mmu 不必通过自身到达那里,这不是鸡和蛋的事情,操作系统根本不会将该内存分配给任何人,它会保护它。 mmu 可以结合虚拟地址的 id 和上半部分来查找 mmu 表条目,或者在单线程系统中可能有一个活动表,操作系统切换使用哪个表或哪个 id 可以访问块,或者让我们这样想,单线程系统可能只有两个 id。这里太模糊了,您需要查看特定的处理器/架构/实现,以了解其工作原理、处理器模式的工作原理、mmu 对这些模式的反应产生的 id 等等。
另一个让我们所有人的生活变得如此轻松的特性是,这也允许应用程序 A 的程序位于 0x00000000,应用程序 B 的程序位于(虚拟地址)0x00000000,应用程序 C 的程序位于 0x00000000,因为他们的物理地址都在不同的地方。但是我们现在可以为该操作系统编译程序,以便它们在相同的内存空间中运行。 Pre-mmu 或没有 mmu,那么 1) 您可能没有受到保护,但 2) 您当然仍然可以拥有带有应用程序的操作系统。
您需要让操作系统移动内存或强制执行与位置无关的代码,以便在启动每个应用程序时,要么从已知地址开始,但操作系统已将另一个应用程序移开/交换,要么与位置无关,并且每个应用程序从不同的空间开始。为了支持内存分配,操作系统需要更加努力地跟踪,并尝试使用一种算法来避免碎片,有时在应用程序重新分配时必须复制数据。