c - 在 Linux 中对 sbrk(0) 的初始调用是否总是返回一个对齐到 8 字节的值(或 4 在 32 位系统的情况下)

标签 c linux gcc

我正在研究此处定义的 malloc 的实现:

http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf .

作者创建了一个自然对齐 4 字节边界的元数据结构,然后将 x 字节的请求对齐到元数据结构之后的 4 字节边界,该元数据结构有效地充当 block 的 header 。 pdf 指出,由于元数据和请求现在已对齐,因此生成的数据将完全对齐。 如果对 sbrk() 的第一次调用返回与 4 字节边界对齐的堆基地址,则结果有效。 sbrk() 是否总是在初始调用中返回 4 字节(或 64 位系统中的 8 字节)对齐地址?

最佳答案

standard for brk and sbrk明确不指定返回的地址是否以任何方式对齐。在 Mac OS X(可能还有其他 BSD 系统)上,大小/地址是页面对齐的,但在 Linux 上,没有这样的舍入发生,可以很容易地用这个小程序测试:

#include <unistd.h>
#include <stdio.h>

int main() {
        void *p;
        p = sbrk(0);
        printf("Initial brk: %p\n", p);
        p = sbrk(1); // Increase the brk (returns OLD brk!)
        p = sbrk(0); // Get the new brk
        printf("New brk: %p\n", p);

        return 0;
}

在我的一个系统上,输出是:

Initial brk: 0x602000
New brk: 0x602001

但是您要求进行初始 通话。 Linux 手册页指出:

brk() and sbrk() change the location of the program break, which defines the end of the process's data segment (i.e., the program break is the first location after the end of the uninitialized data segment). Increasing the program break has the effect of allocating memory to the process; decreasing the break deallocates memory.

单元化数据段也称为 BSS。这里的关键字是 segment,因此很可能初始值始终是页面对齐的。

为了安全起见,可以通过对页面大小(可以通过getpagesize查询)取模来验证初始地址。


更新:所以我很好奇并进一步挖掘。在手册页中,我已经读到 brksbrk 是在内核的 sys_brk 上实现的。它在内核源代码中的实现可以在 mm/mmap.c 中找到(或 mm/nommu.c 对于没有内存管理单元的系统;我们将忽略这个).在 mm/mmap.cbrk 实现中,我们找到这一行:

newbrk = PAGE_ALIGN(brk);

(这里的“brk”是参数,而不是函数。)因此内核确实进行了页面对齐……有点:同时计算是使用页面对齐值和任何必要的内存完成的分配是页面对齐的,brk 的存储值实际上是您传递的指针值:

mm->brk = brk;

因此在用户空间中,即使内核发生了,它看起来也没有发生任何页面对齐。我查看了版本 3.17.5 和 2.4.37,行为是相同的。

关于初始值,在 fs/binfmt_elf.c(实现 ELF 链接)中,我们找到一个函数 set_brk,它设置初始“brk”值(mm->start_brk).该值是显式页面对齐的。处理旧 a.out 格式的 fs/binfmt_aout.c 和处理 HP-UX SOM 格式的 fs/binfmt_som.c 也是如此(从未听说过)前)。还有 fs/binfmt_flat.c 设置初始 brk 值但没有显式对齐;该值在此处隐式对齐。所以看起来初始值总是页面对齐。至少它保证了 ELF 文件的页面对齐,这是我们对“正常”系统关心的。

glibc 简单地包装了 sys_brk 并添加了簿记以正确地实现 sbrk。所以 glibc 的 brk 行为是内核的行为,返回值 sys_brk 存储在内部隐藏变量 __curbrk 中,因此 sbrk 可以正确计算新地址。

关于c - 在 Linux 中对 sbrk(0) 的初始调用是否总是返回一个对齐到 8 字节的值(或 4 在 32 位系统的情况下),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27340800/

相关文章:

linux - 如何为行编写 while 循环

linux - 如何在 Linux 中用美元符号 $ 替换文本字符串?

c - 为什么编译器要向已经 4 字节对齐的结构添加填充?

c - C 中无符号 char 1 的补码

c - 是否可以使用指针从一个函数调用另一个函数的局部变量

c - 简单的C计算器错误

c++ - 在 C 或 C++ 中使用逗号作为宏名称

c++ - 就嵌入式系统的大小而言,我可以获得多小的完整编译器(如 clang 或 gcc)?

c - 堆栈分配、填充和对齐

c - 编译 gcc-5.2.0 时出现问题