brk()系统调用是做什么的?

根据Linux程序员手册:

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

数据段在这里意味着什么? 它只是数据段或数据,BSS和堆的组合?

根据维基:

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

我没有理由改变数据段的大小。 如果它是数据,BSS和堆集体然后它是有道理的,因为堆将获得更多的空间。

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


我看到很多部分的答案,但没有完整的答案。 这是您再次发布的图片:

虚拟内存布局的简化图像

“break” - 由brksbrk操作的地址 - 是堆顶部的虚线。 您读过的文档将此描述为“数据段”的末尾,因为在传统(预共享库,预mmap )Unix中,数据段与堆是连续的; 在程序启动之前,内核会将“文本”和“数据”块从地址0(实际上高于地址0,因此NULL指针真正没有指向任何东西)加载到RAM中,并将中断地址设置为数据段的结尾。 第一次调用malloc会使用sbrk来移动分隔sbrk ,并在数据段顶部和新的更高断点地址之间创建堆,如图所示,随后使用malloc将使用它来创建堆是必要的。

同时,堆栈从内存顶部开始并逐渐减小。 堆栈不需要明确的系统调用来使其更大; 要么开始分配尽可能多的RAM(这是传统的方法),要么在堆栈下面有一个保留地址区域,当它发现尝试在那里写入内容时,内核会自动为其分配RAM (这是现代的方法)。 无论哪种方式,地址空间底部都可能有或没有可用于堆栈的“保护”区域。 如果这个区域存在(所有现代系统都这样做),它永远不会被映射; 如果堆栈或堆试图进入它,你会得到一个分段错误。 然而,传统上,内核并未尝试强制实施边界; 堆栈可能会堆积到堆中,或者堆可能堆积到堆栈中,并且无论如何他们会在彼此的数据上涂写,程序会崩溃。 如果你非常幸运,它会立即崩溃。

我不确定这个图中512GB的数字来自哪里。 它意味着一个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

为了更好地理解地址空间,您应该让自己熟悉分页:x86分页如何工作?

更多信息

brk是POSIX,但在POSIX 2001中被删除,因此需要_GNU_SOURCE来访问glibc包装器。

删除可能是由于引入mmap ,它是一个允许分配多个范围和更多分配选项的超集。

在内部,内核决定进程是否可以拥有那么多的内存,并为此使用内存页面。

brkmmap是libc用于在POSIX系统中实现malloc的常见底层机制。

这解释了堆栈如何与堆进行比较:在x86程序集的寄存器中使用的push / pop指令的功能是什么?


你可以使用brksbrk来避免每个人都在抱怨的“malloc开销”。 但是你不能轻易地将这个方法与malloc结合使用,所以只有在你不需要free任何东西时才适用。 因为你不能。 此外,你应该避免任何可能在内部使用malloc库调用。 IE浏览器。 strlen可能是安全的,但fopen可能不是。

像调用malloc一样调用sbrk 。 它返回一个指向当前中断的指针并按该数量递增中断。

void *myallocate(int n){
    return sbrk(n);
}

虽然不能释放单个分配(因为没有malloc开销,请记住),但可以通过首先调用sbrk返回的值调用brk来释放整个空间,从而倒回brk。

void *memorypool;
void initmemorypool(void){
    memorypool = sbrk(0);
}
void resetmemorypool(void){
    brk(memorypool);
}

你甚至可以叠加这些区域,通过将中断倒退到该区域的开始来丢弃最近的区域。


还有一件事 ...

sbrk在代码高尔夫中也很有用,因为它比malloc短2个字符。

链接地址: http://www.djcxy.com/p/86513.html

上一篇: What does the brk() system call do?

下一篇: what happens when you invoke malloc() on a unix system