哪里定义了内存段?

时间:2016-12-12 04:01:14

标签: c compilation embedded

我刚学会了不同的内存段,如:Text,Data,Stack和Heap。我的问题是:

1-这些部分之间的界限是什么?是编译器还是操作系统?

2-编译器或操作系统如何知道属于每个部分的地址?我们应该在任何地方定义吗?

3 个答案:

答案 0 :(得分:4)

对于您的查询: -

这些部分之间的界限是什么?是编译器还是操作系统?

答案是操作系统。

.text段(可执行代码),.data段(变量)和其他程序段的布局没有通用的寻址方案。但是,程序本身的布局是根据将执行程序的系统(OS)而形成的。

编译器或操作系统如何知道属于每个部分的地址?我们应该在任何地方定义吗?

我将这个问题分成3个问题: -

关于文字(代码)和数据部分及其限制?

文本和数据由编译器准备。编译器的要求是确保它们可访问并将它们打包在地址空间的下半部分。可访问的地址空间将受到硬件的限制,例如:如果指令指针寄存器是32位,那么文本地址空间将是4 GiB。

关于堆部分和限制?它是可用的总RAM内存吗?

在文本和数据之后,上面的区域就是堆。使用虚拟内存,堆实际上可以在接近最大地址空间的地方增长。

堆栈和堆是否有静态大小限制?

进程地址空间中的最后一个段是堆栈。堆栈占用地址空间的末尾段,并从末尾开始向下扩展。

由于堆增长并且堆栈增长,它们基本上相互限制。此外,因为两种类型的段都是可写的,所以它们中的一个跨越边界并不总是违反,因此您可能有缓冲区或堆栈溢出。现在有机制阻止它们发生。

每个进程的堆(堆栈)都有一个设置限制。可以在运行时更改此限制(使用brk()/ sbrk())。基本上发生的是当进程需要更多堆空间并且已经用完已分配的空间时,标准库将向OS发出调用。操作系统将分配一个页面,该页面通常由用户库管理以供程序使用。即如果程序需要1 KiB,操作系统将提供额外的4 KiB,并且该库将为该程序提供1 KiB,并且当程序下次要求更多时,将剩余3 KiB。

大多数情况下,布局将是Text,Data,Heap(成长),未分配的空间,最后是Stack(向下扩展)。它们都共享相同的地址空间。

答案 1 :(得分:4)

这个答案来自一个更专用的嵌入式系统,而不是运行Linux等操作系统的更通用的计算平台。

  

这些部分之间的界限是什么?是编译器还是操作系统?

编译器和操作系统都不这样做。 链接器决定了内存部分的位置。编译器从源代码生成目标文件。链接器使用链接描述文件在内存中查找目标文件。链接描述文件(或链接程序指令)文件是项目的一部分,用于标识各种内存类型(如ROM和RAM)的类型,大小和地址。链接器程序使用链接描述文件中的信息来了解每个内存的起始位置。然后,链接器将每种类型的内存从目标文件定位到适当的内存部分。例如,代码位于.text部分,该部分通常位于ROM中。变量位于RAM中的.data或.bss部分。堆栈和堆也进入RAM。当链接器填充一个部分时,它会学习该部分的大小,然后可以知道从哪个部分开始。例如,.bss部分可能从.data部分结束的地方开始。

可以在链接描述文件中指定堆栈和堆的大小,也可以在IDE中指定项目选项。

嵌入式系统的IDE通常在您创建项目时自动提供通用链接描述文件。通用链接器文件适用于许多项目,因此您可能永远不必自定义它。但是,当您进一步自定义目标硬件和应用程序时,您可能会发现还需要自定义链接描述文件。例如,如果向板上添加外部ROM或RAM,则需要将有关该内存的信息添加到链接描述文件中,以便链接器知道如何在那里找到内容。

链接器可以生成一个映射文件,该文件描述了每个部分在内存中的位置。默认情况下可能不会生成地图文件,如果要查看,可能需要打开构建选项。

  

编译器或操作系统如何知道哪个地址属于每个部分?

嗯,我不相信编译器或操作系统实际上知道这些信息,至少在你可以查询信息的意义上。编译器在链接器找到内存部分之前已完成其工作,因此编译器不知道该信息。操作系统,我该怎么解释呢?嵌入式应用程序甚至可能不使用操作系统。操作系统只是为应用程序提供服务的一些代码。操作系统不知道也不关心内存部分的边界。所有这些信息在操作系统运行时已经被烘焙到可执行代码中。

  

我们应该在任何地方定义吗?

查看链接描述文件(或链接器指令)文件并阅读链接器手册。链接器脚本输入到链接器并提供内存的粗略轮廓。链接器定位内存中的所有内容并确定每个部分的范围。

答案 2 :(得分:2)

这些部分由与OS松散关联的格式定义。例如,在Linux上,您拥有ELF,而在Mac OS上,您拥有Mach-O

99.9%的情况下,您没有明确将这些部分定义为程序员。编译器知道放在哪里。