Memory 编译器如何在内存中布局代码

Memory 编译器如何在内存中布局代码,memory,assembly,compiler-construction,operating-system,virtual-memory,Memory,Assembly,Compiler Construction,Operating System,Virtual Memory,好的,我有一个noob学生的问题 所以我很熟悉堆栈包含子程序调用,堆包含可变长度的数据结构,全局静态变量被分配给永久内存位置 但在理论层面上,这一切又是如何运作的呢 编译器只是假设它有一个从地址0到地址无穷大的整个内存区域吗?然后开始分配东西 它在哪里布局指令、堆栈和堆?在内存区域的顶部,内存区域的末尾 那么虚拟内存是如何工作的呢?虚拟内存对程序是透明的吗 很抱歉问了这么多问题,但我正在学习编程语言结构,它一直在提到这些区域,我想从更实际的角度来理解它们 非常感谢 视情况而定 如果您正在编译一个

好的,我有一个noob学生的问题

所以我很熟悉堆栈包含子程序调用,堆包含可变长度的数据结构,全局静态变量被分配给永久内存位置

但在理论层面上,这一切又是如何运作的呢

编译器只是假设它有一个从地址0到地址无穷大的整个内存区域吗?然后开始分配东西

它在哪里布局指令、堆栈和堆?在内存区域的顶部,内存区域的末尾

那么虚拟内存是如何工作的呢?虚拟内存对程序是透明的吗

很抱歉问了这么多问题,但我正在学习编程语言结构,它一直在提到这些区域,我想从更实际的角度来理解它们

非常感谢

视情况而定

如果您正在编译一个引导加载程序,它必须从头开始,那么您可以假设您已经拥有了自己的全部内存

另一方面,如果你正在编译一个应用程序,你可以假设你已经拥有了自己的全部内存

微小的区别是,在第一种情况下,你拥有自己的所有物理内存。作为引导加载程序,RAM中还没有其他东西。在第二种情况下,内存中有一个操作系统,但它(通常)会为您设置虚拟内存,这样您就可以拥有自己的整个地址空间。不过,通常你仍然需要向操作系统请求实际内存


后者确实意味着操作系统强加了一些规则。操作系统非常想知道你的程序的第一条指令在哪里。一个简单的规则可能是程序总是从地址0开始,因此C编译器可以将
int main()
放在那里。操作系统通常希望知道堆栈在哪里,但这已经是一个更灵活的规则。就“堆”而言,操作系统真的不在乎。

一个全面的解释可能超出了本论坛的范围。全文都是关于这个主题的。然而,在一个简单的层面上,你可以这样看待它

编译器不会将代码放在内存中。它确实假设它自己拥有整个内存区域。编译器生成对象文件,其中对象文件中的符号通常从偏移量0开始

链接器负责将对象文件拉到一起,将符号链接到链接对象内的新偏移位置,并生成可执行文件格式

链接器也不会在内存中布局代码。它将代码和数据打包到通常标记为
.text
的部分,用于可执行代码指令,标记为
.data
的部分用于全局变量和字符串常量。(还有其他部分也用于不同的目的)链接器可以向操作系统加载程序提供将符号重新定位到何处的提示,但加载程序不必这样做

是操作系统加载器解析可执行文件并决定代码和数据在内存中的布局。其位置完全取决于操作系统。通常,堆栈位于比程序指令和数据更高的内存区域,并向下增长

每个程序的编译/链接都假定它自己拥有整个地址空间。这就是虚拟内存的用武之地。它对程序完全透明,完全由操作系统管理

虚拟内存的范围通常从地址0到平台支持的最大地址(不是无限大)。这个虚拟地址空间由操作系统划分为内核可寻址空间和用户可寻址空间。假设在一个32位操作系统上,上面的地址
0x8000000
为操作系统保留,下面的地址供程序使用。如果程序试图访问该分区上的内存,它将被中止

操作系统可能会决定堆栈从最高的可寻址用户内存开始,然后随着位于较低地址的程序代码而增长


堆的位置通常由构建程序所依据的运行时库管理。它可以从程序代码和数据之后的下一个可用地址开始运行。

这是一个有很多主题的开放性问题

假设使用典型的编译器->汇编程序->链接器工具链。编译器不知道太多,它只是对堆栈相关的内容进行编码,不关心堆栈的数量或位置,这就是堆栈的用途/优点,不关心。编译器生成汇编器汇编器被组装成一个对象,然后链接器获取某种风格的信息链接器脚本或命令行参数,当您

gcc hello.c -o hello
您安装的binutils有一个默认的链接器脚本,该脚本是根据您的目标(windows、mac、linux,无论您在什么平台上运行)定制的。该脚本包含有关程序空间从何处开始的信息,然后它知道从何处开始堆(在文本、数据和bss之后)。堆栈指针可能由该链接器脚本设置和/或由操作系统以其他方式管理。这定义了堆栈

对于带有mmu的操作系统,即windows、linux、mac和bsd笔记本电脑或台式电脑所具有的mmu,那么,如果每个程序都有自己的地址空间(从0x0000开始),则会对其进行编译,但这并不意味着该程序链接到0x0000开始运行,操作系统规则是什么取决于操作系统,例如,有些规则从0x8000开始

对于一个类似桌面的应用程序,从程序的角度来看,它是一个线性地址空间,您可能会先有.text,然后是.data或.b