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

dra*_*r40 5 c linux gcc

我正在研究这里定义的malloc的实现:

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

作者创建了一个自然地在4字节边界上对齐的元数据结构,然后在元数据结构之后将x字节的请求与4字节边界对齐,该元数据结构实际上充当块的头部.pdf指出,由于元数据和请求现在已对齐,因此结果数据将完全对齐.如果对sbrk()的第一次调用返回与4字节边界对齐的堆的基址,则结果可以解决.sbrk()总是在初始调用中返回4字节(或64位系统的情况下为8字节)对齐的地址吗?

Dar*_*ust 6

标准brksbrk明确不指定任何方式返回的地址是否一致.在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;
}
Run Code Online (Sandbox Code Playgroud)

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

Initial brk: 0x602000
New brk: 0x602001
Run Code Online (Sandbox Code Playgroud)

但你问了最初的电话.Linux手册页指出:

brk()和sbrk()更改程序中断的位置,该程序中断定义了进程数据段的结束(即,程序中断是未初始化数据段结束后的第一个位置).增加程序中断会为进程分配内存; 减少休息时间会释放内存.

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

如果您想要安全并检查,您可以通过采用页面大小的模数来验证初始地址(您可以通过查询getpagesize).


更新:所以我好奇并且挖了一点.在man-page中,我已经阅读过brksbrk在内核上实现了sys_brk.它可以在内核源代码中找到mm/mmap.c(或者mm/nommu.c对于没有内存管理单元的系统;我们将忽略它).在brk实现中mm/mmap.c,我们找到这一行:

newbrk = PAGE_ALIGN(brk);
Run Code Online (Sandbox Code Playgroud)

("brk"这里是参数,而不是函数.)所以内核确实进行页面对齐...排序:虽然计算是使用页面对齐的值完成的,并且任何必要的内存分配是页面对齐的,但是存储的值是brk实际上是你传递的指针值:

mm->brk = brk;
Run Code Online (Sandbox Code Playgroud)

因此,在用户空间中,即使内核确实发生了任何页面操作,它看起来也不像.我看了3.17.5和2.4.37版本,行为是一样的.

关于初始值,在fs/binfmt_elf.c(实现ELF链接)中,我们找到一个set_brk设置初始"brk"值(mm->start_brk)的函数.该值明确是页面对齐的.对于fs/binfmt_aout.c处理旧的a.out格式以及fs/binfmt_som.c处理HP-UX SOM格式(以前从未听说过)的情况也是如此.还有fs/binfmt_flat.c哪些设置初始brk值但没有明确对齐; 这里的值是隐式对齐的.所以看起来初始值始终是页面对齐的.至少它保证与ELF文件页面对齐,这是我们关心的"普通"系统.

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