7

我正在研究给定进程的内存布局。我注意到每个进程的起始内存位置不是0。在这个网站上,TEXT 从0x08048000开始。一个原因可能是用 NULL 指针来区分地址。我只是想知道是否还有其他好的理由?谢谢。

4

3 回答 3

4

空指针实际上不必为 0。在 C 标准中保证,当在指针的上下文中给出 0 值时NULL,编译器会将其视为。

但是您在源代码中使用的 0 只是语法糖,与空指针值“指向”的实际物理地址无关。

有关详细信息,请参阅:

操作系统上的应用程序有其独特的地址空间,它将其视为连续的内存块(内存在物理上不是连续的,它只是操作系统给每个程序的“印象”)。

在大多数情况下,每个进程的虚拟内存空间以相似且可预测的方式布局(这是 Linux 进程中的内存布局,32 位模式):

Linux进程中的内存布局 (图片来自内存中程序的剖析

查看文本段(基于 x86 的默认 .text 为 0x08048000,由默认链接描述文件选择用于静态绑定)。

为什么是神奇的 0x08048000?可能是因为 Linux 从 System V i386 ABI 借用了该地址。

... 为什么 System V 使用 0x08048000?

选择该值以容纳 .text 部分下方的堆栈,向下增长。0x48000 字节可以由 .text 部分已经需要的相同页表映射(因此在大多数情况下保存页表),而剩余的 0x08000000 将为需要堆栈的应用程序留出更多空间。

0x08048000以下有什么吗?可能什么都没有(只有 128M),但您几乎可以使用 mmap() 系统调用映射任何您想要的东西

也可以看看:

于 2014-10-09T08:07:19.523 回答
2

我想总结一下:

每个进程都有自己的一组页表,但有一个问题。启用虚拟地址后,它们将应用于机器中运行的所有软件,包括内核本身。因此,必须为内核保留一部分虚拟地址空间。

因此,虽然进程获得了自己的地址空间。如果不向内核分配块,它将无法寻址内核代码和数据。

这始终是它出现的第一个内存块,因此包括地址 0。用户模式空间开始于此之外,因此堆栈和堆都驻留于此。

NULL与指针区别

即使用户模式空间从地址开始0,也不会有任何数据分配给该地址0,因为这些数据将在堆栈或堆中,它们本身并不从用户区域的开头开始。因此NULL(具有 的值0)仍然可以使用,而不是这种布局的原因。

然而,与第一个块是内核内存相关的一个好处NULL是任何读取/写入 NULL 的尝试都会引发分段错误。

于 2014-10-09T07:50:44.873 回答
0

加载程序将二进制文件分段加载到内存中:文本(常量)、数据、代码。没有必要从 0 开始,并且由于 C 存在访问 null 周围的错误的问题,这样a[i]甚至是危险的。这允许(在某些处理器上)拦截分段错误。

这将是 C 运行时从 0 引入线性地址空间。这可能是可以想象的,其中 C 是操作系统的实现语言。但毫无用处;让堆从 0 开始。内存模型是段之一。代码段可能受到某些处理器的保护,不会被修改。

段分配发生在 C 运行时管理的内存块中。

我可能会补充一点,操作系统本身经常使用物理 0 及更高的值。

于 2014-10-09T07:49:58.983 回答