【问题标题】:How does sbrk() work in C++?sbrk() 在 C++ 中是如何工作的?
【发布时间】:2011-01-05 19:12:01
【问题描述】:

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

具体是如何工作的?

在什么情况下我想使用sbrk() 来代替繁琐的malloc()new()

顺便说一句,sbrk() 的扩展是什么?

【问题讨论】:

    标签: c++ malloc sbrk


    【解决方案1】:

    看看the specification for brk/sbrk

    该调用基本上要求操作系统通过将先前的“中断值”增加一定量来为应用程序分配更多内存。此数量(第一个参数)是您的应用程序随后获得的额外内存量。

    大多数基本的 malloc 实现都建立在 sbrk 系统调用之上,以获取它们拆分和跟踪的内存块。 mmap 函数通常被认为是更好的选择(这就是为什么像 dlmalloc 这样的 malloc 支持同时使用 #ifdef 的原因)。

    至于“它是如何工作的”,在其最简单级别的 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 规范 (opengroup.org/onlinepubs/9699919799/toc.htm),它们 (brk, sbrk) 不存在。
    • 好点 - 如果实现决定不关心向后兼容性,使用此类函数很可能会导致可移植性问题。
    【解决方案2】:

    sbrk 已经过时了,现在你会使用 mmap 将一些页面映射到 /dev/zero 之外。它当然不是你用来代替 malloc 和朋友的东西,它更像是一种实现它们的方式。此外,当然,它只存在于基于 posix 的操作系统上,这些操作系统关心与古代代码的向后兼容性。

    如果您发现 Malloc 和 New 过于繁琐,则应改为考虑垃圾收集...但请注意,这样做可能会降低性能,因此您需要了解自己在做什么。

    【讨论】:

      【解决方案3】:

      您永远不想使用sbrk 而不是mallocfree。它是不可移植的,通常仅由标准 C 库的实现者或在它不可用的情况下使用。它在man page 中有很好的描述:

      说明

      brk() 设置结束 数据段到指定的值 end_data_segment,当该值为 合理,系统确实有 足够的内存和进程没有 超过其最大数据大小(请参阅 setrlimit(2))。

      sbrk() 增加程序的数据 按增量字节的空间。 sbrk() 不是 一个系统调用,它只是一个 C 库 包装。使用 sbrk() 调用 增量为 0 可用于查找 程序中断的当前位置。

      返回值

      成功时,brk() 返回 零,并且 sbrk() 返回一个指向 新区域的开始。出错时, 返回 -1,并将 errno 设置为 ENOMEM。

      最后,mallocfree 并不繁琐——它们是在 C 中分配和释放内存的标准方式。即使你想实现自己的内存分配器,最好只使用 malloc 和 @ 987654328@ 作为基础 - 一种常见的方法是使用 malloc 一次分配一个大块并从中提供内存分配(这是子分配器或池通常实现的)


      重新命名sbrk(或其表亲brk)的由来,它可能与堆的末尾由一个称为“break”的指针标记的事实有关。堆在 BSS 段之后开始,通常向堆栈增长。

      【讨论】:

        【解决方案4】:

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

        sbrk 用于将 C 库粘合到底层系统的操作系统内存管理。所以进行操作系统调用而不是使用 sbrk()。至于它是如何工作的,这取决于系统。例如,如果您使用 Newlib C 库(通常用于带有 GNU 编译器的“裸机”嵌入式系统),您必须 implement sbrk yourself,所以它在这些情况下的工作方式取决于您,只要它实现它需要扩展堆或失败的行为。

        正如您从链接中看到的那样,它并没有做太多的事情,并且直接使用会非常很麻烦 - 您最终可能会将它包装在 malloc 和 new 提供的所有功能中无论如何。

        【讨论】:

          【解决方案5】:

          这取决于你所说的 malloc 是“繁琐”的意思。 sbrk 通常不再直接使用,除非您正在实现自己的内存分配器:IE,运算符覆盖“new”。即便如此,我也可能会使用 malloc 来给我最初的记忆。

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

          不过,在现代系统上,您不应该触摸此界面。由于您调用 malloc 和 new 很麻烦,我怀疑您没有安全和正确地为您的代码使用 sbrk 的所有必要经验。

          【讨论】:

            猜你喜欢
            • 2017-06-14
            • 2010-11-03
            • 2011-07-03
            • 1970-01-01
            • 1970-01-01
            • 2017-11-08
            • 1970-01-01
            • 1970-01-01
            • 2015-01-09
            相关资源
            最近更新 更多