内存段与物理内存RAM

5

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

  • 我想知道这些段只是为了进程方便而抽象出来的,物理内存是否只是一个线性地址数组,还是物理内存也被分成了这些段?
  • 如果内存没有被分段,只是一个线性地址数组,那么操作系统如何为进程提供这些段的抽象?
  • 如果进程的内存映射以线性数组的形式呈现,而不是分成不同的段(通过MMU将虚拟地址转换为物理地址),编程会有什么变化?

从C(++)程序员的角度来看,内存似乎是由内存管理器管理的线性数组,因此这部分对我来说没有意义。整个问题非常宽泛,显示出很少的研究努力。请查看类似的Stack Overflow问题,然后尝试缩小这个问题的范围。http://stackoverflow.com/search?q=%5Boperating-system%5D+paging+segmentation+answers%3A1.. - xmojmr
@xmojmr 是的,它看起来像一个线性数组,但它被分成了多个段。 - lychee
为什么这个问题被投票关闭?我不认为主题太广泛。但绝对不是一个C语言问题。我认为你应该删除那个标签。 - m0skit0
@m0skit0,我加入了C语言,因为我是从C程序员的角度提问的。 - lychee
正如已经两次说明的那样,从C程序员的角度来看,你的问题毫无意义。这就像因为你从男性角度提问而添加一个“男性”标签一样。 - m0skit0
显示剩余2条评论
4个回答

6
在支持虚拟内存的现代操作系统中,进程的地址空间被划分成这些段。一般情况下,进程的地址空间以完全随机的方式映射到物理RAM上(通常以固定的粒度,例如4K)。相邻的地址空间页不需要映射到相邻的物理RAM页中。物理RAM页不需要保持与进程地址空间页相同的相对顺序。这意味着RAM中没有这样的分段,也不可能有。
为了优化内存访问,操作系统可能会尝试将进程地址空间的连续页面映射到RAM的连续页面中,但这只是一种优化。一般情况下,映射是不可预测的。而且,RAM由系统中所有进程共享,属于不同进程的RAM页面在RAM中任意交错,这消除了在RAM中具有这样的“段”的任何可能性。RAM中没有进程特定的排序或分段。RAM只是虚拟内存机制的高速缓存。
同样,每个进程都使用自己的虚拟地址空间。这就是这些段可以存在的地方。进程无法直接访问RAM。进程甚至不需要知道RAM的存在。

@xmojmr:有什么东西可以描述虚拟内存的基础知识吗?甚至维基百科? - AnT stands with Russia
@m0skit0 我的意思是答案中包含了很多以有争议的“相信我”风格为基础的主张。所谓证据/参考资料,最好是可点击的超链接,指向支持这些主张的可信信息来源,甚至更好的是指向某个“现代操作系统”的源代码,以便清晰地看到所述行为。至少应该提供一些“字典术语”,让OP可以在http://wiki.osdev.org等词典中查找。我不知道“可验证证据”的正确英文术语,所以我使用了“证据”。 - xmojmr
@xmojmr:我所提出的论点是关于虚拟内存如何运作以及RAM在其中扮演角色的基础性“幼儿园级别”描述。这不是一种“相信我”的回答,而是一种“人人都知道…”的回答。 - AnT stands with Russia
@AndreyT 我并不是在说你的回答没有用。我想说的是,你没有提供任何关于“大家都知道”的进一步阅读链接。可以加上一些吗?顺便说一下:OP所称的段“_..bss、data和text.._”被可执行映像加载器用来设置进程的初始内存,例如,bss表示什么也不做,只分配一堆零,text表示分配只读共享内存,现代64位操作系统使用带有平面内存布局的long mode不使用硬件段寄存器。这是“有争议的”部分,因为你所描述的是paging - xmojmr
谷歌搜索“虚拟内存管理”得到了“约8,370,000个结果”。肯定有一两个是有用的吧? - Martin James
显示剩余6条评论

2
这些段主要是为程序加载器和操作系统提供方便的(尽管它们也为粗粒度保护提供了基础;执行权限可以限制在文本上,写入可以禁止从rodata)。1 物理内存地址空间可能会被分割,但不是为了这些应用程序段。例如,在NUMA系统中,硬件可能方便地使用特定位来指示哪个节点拥有给定的物理地址。
对于使用地址转换的系统,操作系统可以在物理内存中任意放置段。(使用分段翻译时,外部碎片可能是一个问题;连续的物理内存地址范围可能不可用,需要昂贵的移动内存段。使用分页翻译,外部碎片是不可能的。分段翻译的优点是需要较少的翻译信息:每个段只需要一个基址和边界以及其他元数据,而内存部分通常有许多超过两个页面,每个页面都有一个基地址和元数据。)
没有地址转换,段的放置必然不太随意。幸运的是,大多数程序不关心段放置的具体地址。(单地址空间操作系统
(请注意,共享部分位于固定位置可能是方便的。对于代码,这可以用于避免通过全局偏移表进行间接引用,而无需在程序加载器/动态链接器中进行二进制重写。这也可以减少地址转换开销。)
应用级编程通常已经足够抽象化,使其存在不被注意到。但是,纯粹的抽象自然对物理资源使用的强烈优化,包括执行时间,不友好。
此外,编程系统可以选择使用更复杂的数据放置方式(应用程序员无需知道实现细节)。例如,使用协程可能鼓励使用仙人掌/意大利面条堆栈,其中不期望连续性。同样,垃圾收集运行时可能提供额外的地址空间划分,不仅用于保育院,还用于将叶对象(没有引用可回收内存)与非叶对象分离开来(减少标记/扫描的开销)。还不太寻常的是提供两个堆栈段,一个用于数据,其地址未被采取(或至少大小固定),另一个用于其他数据。
1类Unix操作系统中这些段的传统布局(向下增长堆栈)在平坦的虚拟地址空间中将文本放在最低地址处,rodata紧随其后,初始化数据紧随其后,零初始化数据(bss)紧随其后,堆从bss顶部向上增长,栈从应用程序的虚拟地址空间顶部向下增长。
使堆和栈相互增长允许每个(对于使用该地址空间的单个线程!)任意增长。这种放置还允许程序加载器简单地将程序文件复制到从最低地址开始的内存中,按权限分组内存,并有时允许单个全局指针寻址所有全局/静态数据范围(rodata、数据和bss)。

2
一个进程的内存映射看起来像是分成了不同的段(栈、堆、bss、数据和文本)。 这是Unix使用的基本映射方式,其他操作系统可能会使用不同的方案。通常情况下,它们会将进程内存空间分割成用于执行代码、栈、数据和堆数据的不同段。
这些段是为了进程方便而创建和管理的抽象,但物理内存可以按线性地址、分段式、非连续的RAM块等方式进行管理。由操作系统管理整个系统内存空间,以便每个进程可以访问自己的部分。
虚拟内存添加了另一层抽象,使得看起来像是线性内存位置实际上被映射到单独的RAM页面,这些页面可以位于物理地址空间的任何位置。
操作系统通过使用虚拟内存映射硬件来管理所有这些内容。每个进程看到的是其代码、数据、栈和堆段的连续内存区域,但实际上,操作系统将这些段中的页映射到物理RAM页。因此,两个运行相同程序的进程将看到由连续内存段组成的相同虚拟地址空间,但构成这些段的内存页面将被映射到完全不同的物理RAM页面。
但请注意,物理RAM实际上可能不是一个连续的内存块,而可能被分成多个不相邻的块或内存银行。操作系统需要以对进程透明的方式管理所有这些内容。
如果将一个进程的内存映射呈线性数组而不是分成段,那么编程会如何改变?然后MMU只需将这些虚拟地址转换为物理地址。 MMU始终都是这样工作的,将虚拟内存地址转换为物理内存地址。操作系统设置并管理每个进程的每个段的每个页面的映射。例如,每当进程超过其堆栈分配时,操作系统就会捕获一个段错误并将另一个页面添加到该进程的堆栈段中,将虚拟页面映射到可用内存中选择的物理页面。
Virtual memory还允许操作系统将进程页面暂时交换到磁盘上,因此所有正在运行的进程占用的虚拟内存总量可以轻松超过系统实际物理内存RAM空间的总量。只有当前活动执行的进程才能访问实际的物理RAM页。

1
我想知道这些段是否只是为了方便而将进程抽象出来的过程,物理RAM只是一系列线性地址,还是物理RAM也被分成这些段了?
实际上,这高度取决于架构。有些架构会有硬件工具(例如x86的描述符寄存器)将RAM分成段。其他只是在软件中保留此信息(用于此进程的OS内核信息)。此外,某些段的信息在执行时完全无关紧要,它们仅用于代码/数据加载(例如重定位段)。
另外,如果RAM没有被分段,只是一个线性数组,那么操作系统如何为进程提供这些段的抽象?
进程代码从不引用段,他只知道地址,因此操作系统没有什么可以抽象的。
此外,如果将内存映射到进程中,只是作为一个线性数组而不是分成段,那么编程会发生什么变化呢?然后MMU会将这些虚拟地址转换为物理地址。
编程不会受影响。当您使用C语言编程时,您不定义任何这些段,代码也不引用这些段。这些段用于保持有序布局,甚至不需要在各个操作系统上相同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接