【问题标题】:How do Operating Systems prevent programs from accessing memory?操作系统如何阻止程序访问内存?
【发布时间】:2020-03-12 18:23:45
【问题描述】:

我目前的理解是,

  • 我可以用C写操作系统

  • 我可以用 C 语言为那个操作系统编写程序

  • 当我写一个操作系统时,我可以看到所有的内存

  • 当我编写程序时,操作系统会向我隐藏其他程序的内存。

  • 每当一个程序在操作系统中运行时,它对程序的印象就好像它分配的内存是计算机的全部内存

CPU / OS 是如何做到这一点的?这是纯粹在软件级别上实现的吗?还是也需要硬件实现?

【问题讨论】:

  • 为了提高效率,它需要硬件支持;否则,您只能使用执行边界检查的解释器,而不能运行本机机器代码。

标签: assembly operating-system cpu-architecture virtual-memory mmu


【解决方案1】:

这不仅仅是软件层面的。英特尔架构的几句话:

每个进程的地址空间是隔离的;每个进程都有相同的虚拟地址空间(让我们简化一下:0x00000000 到 0xffffffff),映射到不同的物理位置。

地址空间代表内存页面的集合。页面仅在需要时进行物理映射。将长时间未访问的页面(有特殊算法)从物理内存中删除;如果它们包含动态修改的内容,它们将存储在硬盘驱动器上的“交换”文件中。

每个页面都属于特定的进程(除了一些系统页面),分配了虚拟地址和访问标志:读/写/执行。看似连续的数组,可以分配在几个不连续的页面上,其中一些甚至可以换出到硬盘驱动器。

程序(进程)只能看到自己的地址空间。有几种方法可以到达其他进程的空间,但常规程序很少这样做。

地址空间不完全可访问:如果程序试图访问未分配的地址,或写入写保护页,将触发内存冲突。

通常,程序只能在自己的地址空间中分配、取消分配或更改页面的访问标志。有多种类型的内存(用于加载可执行映像、用于堆栈以及用于几种不同风格的可分配内存)。

对不起,我不记得书名了,很久以前看过的。

【讨论】:

  • 关键词:“虚拟内存”、“页面错误”、“页表”、“tlb”。这个答案没有解释硬件支持做这些映射。此外,虚拟内存启用分页/换出冷页,但您可以在没有虚拟内存的情况下拥有虚拟内存。对于具有充足 RAM 的现代 CPU 上的玩具操作系统来说,这当然是合理的。
  • 那么我对操作系统可以看到所有内存的理解不正确吗?因为操作系统运行在与应用程序不同的进程上,所以它同样从内存中隐藏?
  • @Tyler:操作系统可以看到所有内存。不要将其视为一个过程;用户空间进程在内核“下”运行。
  • @Tyler:操作系统只能“看到”所有地址空间中分配的内存。无论如何,它是操作系统的一部分,它存在于每个用户进程中。用户进程只能在自己的地址空间中“看到”分配的内存(除非它做了一些特殊的事情)。您可以“保留”内存;在您“提交”它之前,它将无法访问。我想,这本书的描述是 J. Richter 的“Programming Applications for Microsoft Windows”。
【解决方案2】:

操作系统如何阻止程序访问内存?

简答:在 x86 处理器上,他们通过激活 Protected Mode(32-bit)Long Mode(64-bit) 来实现。 ARM 或其他处理器实现了类似的概念。 保护模式 保护不同进程 的内存空间,让每个进程都有自己的内存空间。这个概念叫做Virtual Memory

在硬件中,这是通过MMU(用于内存)或IOMMU(用于 IO 内存)来实现的,它们阻止对内存空间某些区域的访问。

CPU / OS 是如何做到这一点的?这是纯粹在软件级别上实现的吗?还是也需要硬件实现?

如上所述,这最好在硬件中实现以提高效率。它不能(有效地)纯粹在软件级别上完成。

作为高级读者的思想实验:
尝试在Real Mode中实现进程隔离(防止另一个进程访问这个进程的内存)。

一个(合理的)答案:
我所知道的软件实现的唯一方法是虚拟机,它检查内存访问的所有边界(所有指令的) - 这基本上是 MMU 所做的。

【讨论】:

  • 激活 x86 保护模式与分页是分开的。我的意思是,保护模式即使没有分页也可以通过分段为您提供内存保护,但这不是主流 x86 操作系统所做的。
  • cpu如何跟踪进程?它不只是从内存中运行命令吗?那么它是如何知道命令来自给定进程的呢?
  • CPU 不跟踪进程,它只检查数据和指令的权限。操作系统会这样做——“进程”只是一个抽象层。
【解决方案3】:

当前常见的解决方案是使用 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) 您当然仍然可以拥有带有应用程序的操作系统。

您需要让操作系统移动内存或强制执行与位置无关的代码,以便在启动每个应用程序时,要么从已知地址开始,但操作系统已将另一个应用程序移开/交换,要么与位置无关,并且每个应用程序从不同的空间开始。为了支持内存分配,操作系统需要更加努力地跟踪,并尝试使用一种算法来避免碎片,有时在应用程序重新分配时必须复制数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 1970-01-01
    • 2012-03-02
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多