JL2*_*210 6 c legacy malloc sbrk brk
我在很多地方(MUSL邮件列表,MACOS论坛等)听说brk()和sbrk()不安全。这些地方中的许多要么根本不给出解释,要么给出非常模糊的解释。例如,此链接指出“这些功能从根本上被破坏了”,并继续说malloc和sbrk子系统被完全破坏了,它们破坏了堆,等等。
我的问题是:为什么会这样?如果malloc以这样的方式使用它来分配sbrk足够大的内存块,以平息或大大减少对进一步分配的需求,那么使用sbrk并且brk完全安全吗?
这是我sbrk和的实现brk:
sbrk:
#include <unistd.h>
#include <stddef.h>
void *sbrk(intptr_t inc)
{
        intptr_t curbrk = syscall(SYS_brk, NULL);
        if( inc == 0 ) goto ret;
        if( curbrk < 0 ) return (void *)-1;
        curbrk((void *)(curbrk+inc));
ret:
        return (void *)curbrk;
}
brk:
#include <unistd.h>
intptr_t brk(void *ptr)
{
        if( (void *)syscall(SYS_brk, ptr) != ptr )
                return -1;
        else
                return 0;
}
现实在很大程度上取决于实施,但这里有一些要素:
brk/sbrk被发明来允许进程从系统请求更多内存,并在单个连续段中释放它。因此,他们被许多人使用malloc和free实现。问题是,当它返回一个唯一的段时,当多个模块(同一进程的)直接使用它时,事情会出错。由于竞争条件,它在多线程进程中变得更糟。假设 2 个线程想要添加新内存。他们将查看当前的最高地址sbrk(0),查看相同的地址,使用brk或请求新内存sbrk,并且由于竞争条件,将使用相同的内存。
即使在单线程过程中,有些malloc并free实现假设,他们只被允许使用低级别s/brk接口,以及任何其他代码应该使用它们。在这种情况下,如果他们内部维护的中断段的图像不再是假定值,那么事情就会出错。他们应该猜测段的某些部分被“保留”用于其他用途,可能会破坏释放任何内存的能力。
出于这个原因,用户代码永远不应该直接使用brk/sbrk而只依赖malloc/ free。如果,当且仅当你正在写的标准库的实现,包括malloc/ realloc/ calloc/ free,你可以放心地使用brk/sbrk
在现代系统上,mmap可以更好地使用虚拟内存管理。您可以根据需要使用任意数量的动态内存段,而无需在它们之间进行交互。因此,在现代系统上,除非您对使用brk/进行内存分配有特定需求,否则sbrk您应该使用mmap.
FreeBSD 参考brk并sbrk说明了这一点:
brk() 和 sbrk() 函数是现代虚拟内存管理出现之前的遗留接口。
然后:
错误:将 brk() 或 sbrk() 与 malloc(3)、free(3) 或类似函数混合使用将导致不可移植的程序行为。