内存段和物理 RAM

memory segments and physical RAM

一个进程的内存映射似乎被分成多个段(堆栈、堆、bss、数据和文本),

在支持虚拟内存的现代OS中,进程的地址space被划分为这些段。在一般情况下,进程的地址 space 以完全 随机 的方式投影到物理 RAM 上(具有一些固定的粒度,通常为 4K)。彼此相邻的地址 space 页不必投影到相邻的 RAM 物理页中。 RAM 的物理页面不必保持与进程地址 space 页相同的相对顺序。这一切都意味着在 RAM 中没有这样的段分离,而且不可能存在。

为了优化内存访问,OS 可能(并且通常会)尝试将进程地址 space 的连续页面映射到 RAM 中的连续页面,但这只是一种优化。在一般情况下,映射是不可预测的。最重要的是,RAM 由系统中的所有进程共享,属于不同进程的 RAM 页面在 RAM 中任意交错,这消除了在 RAM 中出现 "segments" 的任何可能性。 RAM 中没有进程特定的排序或分段。 RAM只是虚拟内存机制的缓存。

同样,每个进程都使用自己的虚拟地址 space。这是这些段可以存在的地方。该进程无法直接访问 RAM。该进程甚至不需要知道 RAM 的存在。

I was wondering are these segments are just abstraction for the processes for convience and the physical RAM is just a linear array of addresses or the physical RAM is also fragmented into these segments?

这实际上在很大程度上取决于体系结构。有些将具有硬件工具(例如 x86 的描述符寄存器)来将 RAM 分成多个段。其他人只是将此信息保存在软件中(此进程的 OS 内核信息)。此外,一些段信息与执行完全无关,它们仅用于 code/data 加载(例如重定位段)。

Also if the RAM is not fragmanted and is just a linear array then how the OS provides the process the abstraction of these segments?

进程代码从不引用段,他只知道地址,所以OS没有什么可抽象的。

Also how the programming would change if the memory map to a process would appear just as a linear array and not divided into segments?, and then the MMU would just translate these virtual addresses into physical ones

编程不会受到影响。当你用 C 编程时,你不会定义任何这些段,代码也不会引用这些段。这些段是为了保持有序布局,甚至不需要在 OS.

之间保持相同

这些段在很大程度上为程序加载器和操作系统提供了便利(尽管它们也为粗粒度保护提供了基础;执行权限可以限制为文本,禁止从 rodata 写入)。 1

物理内存地址space可能是碎片化的,但不是为了这样的应用程序段。例如,在 NUMA 系统中,硬件使用特定位来指示哪个节点拥有给定的物理地址可能很方便。

对于使用地址转换的系统,OS 可以在某种程度上任意地将段放置在物理内存中。 (对于分段翻译,外部碎片可能是一个问题;物理内存地址的连续范围可能不可用,需要昂贵的内存段移动。对于分页翻译,外部碎片不是分段翻译的优点是需要较少的翻译信息:每个段只需要一个基址并与其他元数据绑定,而内存部分通常有两个以上的页面,每个页面都有一个基址和元数据。)

如果没有地址转换,段的放置必然会不那么随意。幸运的是,大多数程序并不关心放置段的具体地址。 (单个地址spaceOSes

(请注意,将可共享部分置于固定位置可能 方便 。对于代码,这可用于通过全局偏移量避免间接寻址 table 而无需需要在程序 loader/dynamic 链接器中重写二进制文件。这也可以减少地址转换开销。)

应用程序级编程通常从这样的细分中充分抽象出来,以至于它的存在并不引人注意。然而,纯抽象自然不利于物理资源使用的密集优化,包括执行时间。

此外,编程系统可能会选择使用更复杂的数据放置(应用程序员无需了解实现细节)。例如,协程的使用可能会鼓励使用 cactus/spaghetti 堆栈,其中不需要连续性。类似地,垃圾收集运行时可能会提供地址 space 的额外划分,不仅用于托儿所,而且用于将没有引用 collectable 内存的叶对象与非叶对象分开(减少mark/sweep 的开销)。提供两个堆栈段也不是特别不寻常,一个用于地址未被占用(或至少大小固定)的数据,另一个用于其他数据。


1这些段的一种传统布局(具有向下增长的堆栈)在平面 虚拟 地址中 space类 Unix OSes 将文本放在最低地址,rodata 直接在其上方,初始化数据紧接其上方,零初始化数据(bss)紧接其上方,堆从 bss 顶部向上增长,堆栈增长从应用程序的虚拟地址部分的顶部向下 space。

让堆和堆栈相互增长允许任意增长(对于使用该地址的单个线程 space!)。这种放置还允许程序加载器简单地将程序文件从最低地址开始复制到内存中,按权限对内存进行分组,有时可以允许单个全局指针寻址所有 global/static 数据范围(rodata、data , 和 bss).

The memory map to a process appears fragmented into segments (stack, heap, bss, data, and text)

这是Unix使用的基本映射;其他操作系统使用不同的方案。不过,一般来说,他们将进程内存 space 分成单独的段,用于执行代码、堆栈、数据和堆数据。

I was wondering are these segments are just abstraction for the processes for convience and the physical RAM is just a linear array of addresses or the physical RAM is also fragmented into these segments?

视情况而定。 是的,这些细分是由 OS 为了流程的利益而创建和管理的。但是物理内存可以安排为线性地址、分组段或不连续的 RAM 块。 OS 负责管理总系统内存 space 以便每个进程都可以访问它自己的部分。

虚拟内存增加了另一层抽象,因此看起来像线性内存位置的东西实际上映射到单独的 RAM 页面,这些页面可以位于物理地址中的任何位置 space。

Also if the RAM is not fragmanted and is just a linear array then how the OS provides the process the abstraction of these segments?

OS 通过使用虚拟内存映射硬件来管理所有这些。每个进程都看到其代码、数据、堆栈和堆段的连续内存区域。但实际上,OS 将每个段中的页面映射到 RAM 的物理页面。因此,两个相同的 运行 进程将看到由连续内存段组成的相同虚拟地址 space,但包含这些段的内存页面将映射到完全不同的物理 RAM 页面。

但请记住,物理 RAM 实际上可能不是一个连续的内存块,而实际上可能被分割成多个不相邻的块或内存组。 OS 以对流程透明的方式管理所有这些。

Also how the programming would change if the memory map to a process would appear just as a linear array and not divided into segments?, and then the MMU would just translate these virtual addresses into physical ones.

MMU 始终以这种方式运行,将虚拟内存地址转换为物理内存地址。 OS 为每个进程设置和管理每个段的每个页面的映射。例如,每次进程超出其堆栈分配时,OS 捕获段错误并将另一个页面添加到进程的堆栈段,将虚拟页面映射到从可用内存中选择的物理页面。

虚拟内存还允许 OS 将进程页面临时换出到磁盘,这样所有 运行 进程占用的虚拟内存总量很容易超过实际物理内存系统的 RAM space。只有当前活动的执行进程才能真正访问真正的物理 RAM 页。