根据Linux程序员手册:
brk() 和 sbrk() 改变程序中断的位置,它定义了进程数据段的结束。
这里的数据段是什么意思?只是数据段还是数据、BSS、堆结合?
根据维基数据段:
有时数据、BSS 和堆区域统称为“数据段”。
我认为没有理由只更改数据段的大小。如果是数据、BSS和堆,那么它是有意义的,因为堆将获得更多空间。
这让我想到了第二个问题。在我到目前为止阅读的所有文章中,作者都说堆向上增长,堆栈向下增长。但是他们没有解释的是当堆占据堆和栈之间的所有空间时会发生什么?
在您发布的图表中,“中断”(即他操纵的地址brk)sbrk是堆顶部的虚线。
brk
sbrk
您阅读的文档将其描述为“数据段”的结尾,因为在传统的(预共享库,预mmap)Unix 中,数据段与堆是连续的;在程序启动之前,内核会将“文本”和“数据”块加载到从地址零开始的 RAM 中(实际上稍微高于地址零,因此 NULL 指针确实没有指向任何东西)并将中断地址设置为数据段的结尾。然后第一次调用malloc将sbrk用于移动中断并在数据段顶部和新的更高中断地址 之间 创建堆,如图所示,后续使用malloc将使用它来使堆更大有必要的。
mmap
malloc
同时,堆栈从内存顶部开始向下增长。堆栈不需要显式系统调用来使其更大;要么从分配给它的尽可能多的 RAM 开始(这是传统方法),要么在堆栈下方有一个保留地址区域,当内核注意到尝试写入该区域时,它会自动分配 RAM (这是现代方法)。无论哪种方式,地址空间底部可能有也可能没有可用于堆栈的“保护”区域。如果该区域存在(所有现代系统都这样做),则永久未映射;如果 有的话 堆栈或堆试图增长到它,你得到一个分段错误。但是,传统上,内核并没有尝试强制执行边界。堆栈可以增长到堆中,或者堆可以增长到堆栈中,无论哪种方式,他们都会在彼此的数据上乱涂乱画,程序就会崩溃。如果你非常幸运,它会立即崩溃。
我不确定此图中的数字 512GB 来自何处。它意味着一个 64 位的虚拟地址空间,这与您在那里的非常简单的内存映射不一致。一个真正的 64 位地址空间看起来更像这样:
Legend: t: text, d: data, b: BSS
这不是远程扩展的,也不应该被解释为任何给定操作系统的确切方式(在我画了它之后,我发现 Linux 实际上使可执行文件比我想象的更接近零地址,并且共享库在令人惊讶的高地址)。该图的黑色区域未映射——任何访问都会立即导致段错误——并且相对于灰色区域它们是 巨大的。 浅灰色区域是程序及其共享库(可能有几十个共享库);每个都有 独立的 文本和数据段(以及“bss”段,它也包含全局数据,但初始化为全位为零,而不是占用磁盘上的可执行文件或库中的空间)。堆不再必须与可执行文件的数据段连续——我是这样画的,但至少看起来 Linux 并没有这样做。栈不再与虚拟地址空间的顶部挂钩,堆与栈之间的距离如此巨大,不用担心越过它。
中断仍然是堆的上限。但是,我没有展示的是,在某处黑色的地方可能有几十个独立的内存分配,用mmap而不是brk. (操作系统会尽量让它们远离该brk区域,以免它们发生碰撞。)