BRK()系统调用做什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (47)

根据Linux程序员手册:

brk()和sbrk()更改了程序中断的位置,这定义了进程数据段的结束。

数据段在这里意味着什么?是数据段还是数据、BSS和堆的组合?

根据wiki的说法:

有时,数据、BSS和堆区域统称为“数据段”。

我认为没有理由改变数据段的大小。如果是数据、BSS和堆,那么它就有意义了,因为堆将获得更多的空间。

这就引出了我的第二个问题。在我读过的所有文章中,作者说堆向上增长,堆栈向下增加。但是他们没有解释的是,当堆占据堆和堆栈之间的所有空间时会发生什么?

提问于
用户回答回答于

“中断”---被操纵的地址brksbrk。你读过的文档将此描述为“数据段”的结尾,因为在传统的(预共享库,预共享库)中-mmap)Unix数据段与堆是连续的;在程序启动之前,内核将从地址零开始将“text”和“data”块加载到RAM中(实际上略高于地址零,因此空指针真正没有指向任何内容),并将中断地址设置为数据段的末尾。第一次呼叫malloc然后使用sbrk要移动拆分并创建堆,请执行以下操作介于两者之间数据段的顶部和新的、更高的中断地址,如图所示,以及随后使用的malloc会在必要的时候用它来使堆变大。

同时,堆栈从内存的顶部开始并向下扩展。堆栈不需要显式的系统调用来使其更大;要么它开始时分配给它的RAM尽可能多(这是传统的方法),要么堆栈下面有一个预留地址区域,当内核注意到试图在堆栈上写入内存时,它会自动分配RAM(这是现代的方法)。无论哪种方式,在地址空间的底部都可能有一个“保护”区域,可以用于堆栈。如果存在此区域(所有现代系统都这样做),则永久取消映射;如果任一堆栈或堆试图发展为它,将得到一个分段错误。但是,传统上内核并不试图强制执行边界;堆栈可以发展为堆,堆也可以发展为堆栈,不管是哪种方式,它们都会在彼此的数据上乱涂乱写,程序就会崩溃。如果你很幸运的话,它会立即坠毁。

我不知道这个图表中的512 GB是从哪里来的。它意味着64位虚拟地址空间,这与您在那里的非常简单的内存映射不一致。一个真正的64位地址空间看起来更像这样:

这不是远程扩展的,也不应该被解释为任何给定的操作系统都是如何工作的(在我绘制了它之后,我发现Linux实际上使可执行文件的地址比我想象的接近零,并且共享库的地址高得惊人)。此图的黑色区域未映射--任何访问都会导致立即分段错误--它们是相对于灰色区域。浅灰区域是程序及其共享库(可以有几十个共享库);每个库都有一个独立文本和数据段(和“bss”段,它也包含全局数据,但是初始化为所有位-零,而不是占用磁盘上的可执行文件或库中的空间)。堆不再一定是连续的可执行文件的数据段--我是这样画的,但是看起来至少Linux不这样做。堆栈不再与虚拟地址空间的顶部挂钩,堆与堆栈之间的距离非常大,因此不必担心跨越它。

中断仍然是堆的上限。然而,我没有显示的是,在黑色的某个地方,可能会有几十个独立的内存分配,是用mmap而不是brk。(操作系统将试图使这些远离brk使它们不会碰撞。)

用户回答回答于

最小可运行示例

BRK()系统调用做什么?

请求内核允许读写一个称为堆的连续内存块。

如果你不问的话,这可能会让你受伤。

brk:

#define _GNU_SOURCE
#include <unistd.h>

int main(void) {
    /* Get the first address beyond the end of the heap. */
    void *b = sbrk(0);
    int *p = (int *)b;
    /* May segfault because it is outside of the heap. */
    *p = 1;
    return 0;
}

带着brk:

#define _GNU_SOURCE
#include <assert.h>
#include <unistd.h>

int main(void) {
    void *b = sbrk(0);
    int *p = (int *)b;

    /* Move it 2 ints forward */
    brk(p + 2);

    /* Use the ints. */
    *p = 1;
    *(p + 1) = 2;
    assert(*p == 1);
    assert(*(p + 1) == 2);

    /* Deallocate back. */
    brk(b);

    return 0;
}

在Ubuntu 14.04上测试。

虚拟地址空间可视化

以前brk:

+------+ <-- Heap Start == Heap End

brk(p + 2):

+------+ <-- Heap Start + 2 * sizof(int) == Heap End 
|      |
| You can now write your ints
| in this memory area.
|      |
+------+ <-- Heap Start

brk(b):

+------+ <-- Heap Start == Heap End

扫码关注云+社区