C++中的sbrk()函数是如何工作的?

10

在哪里可以详细了解 sbrk()

sbrk() 是如何工作的?

在什么情况下,我需要使用 sbrk() 而不是繁琐的 malloc()new()

顺便问一下,sbrk() 的扩展是什么?

5个回答

11
请看brk/sbrk的规范
该调用基本上要求操作系统通过增加先前的“断点值”来为应用程序分配更多内存。这个参数(第一个参数)是您的应用程序获得的额外内存量。
大多数基本的malloc实现都建立在sbrk系统调用之上,以获取它们拆分和跟踪的内存块。mmap函数通常被认为是更好的选择(这就是为什么像dlmalloc这样的malloc支持两者的原因)。
至于“它是如何工作的”,在其最简单的级别上,sbrk可能看起来像这样:
uintptr_t current_break; // Some global variable for your application.
                         // This would probably be properly tracked by the OS for the process
void *sbrk(intptr_t incr)
{
    uintptr_t old_break = current_break;
    current_break += incr;
    return (void*) old_break;
}

现代操作系统会做更多的事情,例如将页面映射到地址空间中,并为每个分配的内存块添加跟踪信息。

请注意,参考文献中的函数标记为“LEGACY”;如果您前往当前的SUS规范(http://www.opengroup.org/onlinepubs/9699919799/toc.htm),它们(brk,sbrk)将不再存在。 - Jonathan Leffler
好的观点 - 如果实现决定不关心向后兼容性,使用这样的函数可能会导致可移植性问题。 - Matthew Iselin
我认为这个调用只是触及到下一页,然后操作系统在幕后进行分配。 - excitoon

3

sbrk已经过时了,现在你可以使用mmap来从/dev/zero映射一些页面。它肯定不是你使用malloc和相关函数的替代品,而更多地是用于实现它们的一种方式。当然,它仅存在于关心向后兼容古老代码的基于posix的操作系统中。

如果你觉得Malloc和New太麻烦,你应该考虑使用垃圾回收...但要注意,这可能会带来性能成本,所以你需要了解自己在做什么。


2
你永远不应该使用 sbrk来替代 malloc 或者 free。它是非可移植的,通常只由标准 C 库的实现者或者在没有其他选择时才使用。你可以在其 man page 上找到详细的描述。 描述

brk()将数据段结束位置设置为end_data_segment指定的值,当该值是合理的、系统具有足够的内存并且进程未超过其最大数据大小(参见setrlimit(2))时。

sbrk()通过increment参数增加程序的数据空间。sbrk()不是一个系统调用,它只是一个C库包装器。使用0作为increment参数调用sbrk()可以查找程序断点的当前位置。

返回值

成功时,brk()返回0,sbrk()返回指向新区域开头的指针。失败时返回-1,并将errno设置为ENOMEM。

最后,mallocfree并不繁琐 - 它们是在C语言中分配和释放内存的标准方式。即使您想要实现自己的内存分配器,最好也只使用mallocfree作为基础 - 通常的方法是使用malloc一次性分配大块内存,并从中提供内存分配(这就是子分配器或池通常实现的内容)。
关于名称`sbrk`(或其近亲`brk`)的起源,它可能与堆的末尾由指针标记为“break”的事实有关。 堆紧随BSS段之后开始,通常向上增长到堆栈。

2

你已经标记了这是C++,那么为什么要使用“繁琐”的malloc()而不是new呢?我不确定malloc有什么繁琐的地方;也许在内部有,但你为什么要关心呢?如果你确实关心(例如出于确定性的原因),你可以分配一个大的池子并为该池子实现自己的分配器。当然,在C++中,你可以重载new运算符来实现。

sbrk用于将C库与底层系统的OS内存管理粘合在一起。所以要进行OS调用,而不是使用sbrk()。至于它如何工作,那就取决于系统。例如,如果你正在使用Newlib C库(通常在使用GNU编译器的“裸机”嵌入式系统上使用),你必须自己实现sbrk,因此在这些情况下它的工作方式取决于你,只要它实现了扩展堆或失败的所需行为即可。

正如你从链接中看到的,它并没有做太多的事情,直接使用它会非常繁琐 - 你最终可能会将其包装在malloc和new提供的所有功能中。


0

这取决于您所说的malloc是否“繁琐”。现在通常不直接使用sbrk,除非您正在实现自己的内存分配器:例如,重载“new”运算符。即使如此,我可能也会使用malloc来提供初始内存。

如果您想了解如何在sbrk上实现malloc(),请查看http://web.ics.purdue.edu/~cs354/labs/lab6/,这是一个演示练习。

在现代系统上,您不应该触及此接口。由于您认为malloc和new很繁琐,我怀疑您没有足够的经验来安全、正确地使用sbrk编写代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接