每个程序分为几个部分,例如:
- 代码段
- 数据段
- 堆栈段
- 堆段
但谁负责这一进程?编译器和链接器必须支持与这些段的对话。
- 它是由编译器和链接器开发人员首先实现的并且早期的操作系统使用了它吗?
或者
- 它是由操作系统开发人员首先实现的,然后编译器和链接器开发人员需要进行调整吗?
答案1
这部分是基于 CPU 的设计。到 20 世纪 80 年代,大多数计算机都采用 16 位架构,因此指针可以访问 2 16 (65536) 字节的虚拟地址空间。这自然被认为过于严格。当代码段与其他段分开时,您可以拥有 2 16字节的代码(指令)和 2 16字节的数据,这还不算太糟。
程序/进程的“数据空间”(使用最广泛意义上的术语)可以包括
- 初始化数据,包括
- 字符串(例如
printf("Hello, world\n");
), - 已初始化变量(例如
int i = 42;
),以及 - 在某些情况下,代码中使用的常量(例如
y = x + 17;
),
- 字符串(例如
- 未初始化的变量(例如
int j;
,char mydata[80];
在静态/全局上下文中), - 动态分配的内存(
malloc
、new
)和 - 堆栈(函数参数、保存/恢复信息和局部变量)。
我可能遗漏了一些东西,但寄存器实际上不算数(它们本质上是未初始化变量的一个特例),共享内存超出了本文的讨论范围。请注意,上述内容中,只有初始化数据需要保存在可执行文件中 — 未初始化变量和堆栈的空间由操作系统在执行程序时自动分配。因此,编译器需要分别跟踪初始化数据和未初始化变量。
另一个问题是,Intel 8086 CPU(奔腾的曾祖父)要求每个段都是一个大的连续内存块。Unix 内核由一个大的代码块和一个大的数据块(初始化的数据和未初始化的变量)组成,但它为每个进程使用一个单独的堆栈。因此,8086 需要能够将堆栈放在与所有其他数据不同的段中。
网络上有很多关于此内容的信息。例如,维基百科: