一尘不染

无需过度使用linux上的Malloc

linux

如何在Linux上分配内存而又不过度使用内存,以便NULL在没有可用内存并且进程不会在访问时随机崩溃的情况下,malloc实际上返回?

我对malloc如何工作的理解:

  1. 分配器检查空闲列表是否有可用内存。如果是,则分配内存。
  2. 如果否,则从内核分配新页面。这就是过度使用可能发生的地方。然后返回新的内存。

因此,如果有一种方法可以从立即由物理内存支持的内核中获取内存,则分配器可以使用该内存而不是获取过量使用的页面,NULL如果内核拒绝提供更多的内存,则分配器可以返回。

有办法吗?

更新:

我知道这不能完全保护该过程免受OOM杀手的侵害,因为如果它的得分很差,它仍然会在内存不足的情况下被杀死,但这不是我担心的。

更新2: 名义动物的评论给了我以下使用的想法mlock

void *malloc_without_overcommit(size_t size) {
    void *pointer = malloc(size);
    if (pointer == NULL) {
        return NULL;
    }
    if (mlock(pointer, size) != 0) {
        free(pointer);
        return NULL;
    }

    return pointer;
}

但这可能由于所有系统调用而相当慢,因此,这应该在分配器实现的级别上完成。而且它还会阻止使用交换。

更新3:

根据约翰·博林格斯(John Bollingers)的评论提出了新想法:

  1. 检查是否有足够的内存。据我了解,这必须/proc/meminfoMemFreeSwapFree值中进行检查。
  2. 仅当有足够的可用空间(加上额外的安全裕度)时,才分配内存。
  3. 找出pagesize,getpagesize并在每个pagesize中向内存写入一个字节,以便它得到物理内存(RAM或swap)的支持。

我还仔细查看了mmap(2),发现了以下内容:

MAP_NORESERVE

不要为该映射保留交换空间。当保留交换空间时,可以保证可以修改映射。如果没有保留交换空间,则在没有物理内存可用的情况下,可能会在写入时获得SIGSEGV。另请参阅proc(5)中有关文件/
proc / sys / vm / overcommit_memory的讨论。在2.6之前的内核中,此标志仅对私有可写有效

这是否意味着进行映射~MAP_NORESERVE将完全保护该过程免受OOM杀手的侵害?如果是这样,这是一个完美的解决方案,只要有malloc实现,它就可以直接在之上工作mmap。(也许是jemalloc?)

更新4: 我目前的理解是,~MAP_NORESERVE这不能防止OOM杀手,但至少可以防止在首次写入内存时出现段错误。


阅读 261

收藏
2020-06-03

共1个答案

一尘不染

如何在Linux上分配内存而不过度使用

那是一个加载的问题,或者至少是一个不正确的问题。该问题基于错误的假设,这使得回答所陈述的问题充其量是不相关的,最糟糕的是会引起误解。

内存过量使用是一项系统范围的策略-
因为它确定为进程提供了多少虚拟内存-而不是由进程自己决定的事情。

由系统管理员确定内存是否过量使用。在Linux中,该策略是相当可调整的(例如/proc/sys/vm/overcommit_memory,参见man
5 proc中的内容

在分配期间,没有任何进程会影响内存过量使用策略

OP也似乎对使自己的进程不受Linux中的内存不足杀手(OOM杀手)的影响很感兴趣。(Linux中的OOM杀手是一种通过杀死进程,从而将其资源释放回系统来减轻内存压力的技术。)

这也是不正确的方法,因为OOM杀手是一个启发式过程,其目的不是“惩罚或杀死行为不良的过程”,而是使系统保持运行状态。此功能在Linux中也很可调,而且系统管理员甚至可以调整在内存压力大的情况下每个进程被杀死的可能性。除了进程使用的内存量以外,
还不完全取决于该进程来影响OOM杀手在内存不足的情况下是否将其杀死 。这也是系统管理员管理的策略问题,而不是流程本身。

我认为OP试图解决的实际问题是如何编写可以动态响应内存压力而不只是死掉的Linux应用程序或服务(由于SIGSEGV或OOM杀手的原因)。答案是 您不会
-让系统管理员担心对他们来说重要的事情,而不是他们的工作量-
除非您的应用程序或服务是使用大量内存的应用程序或服务因此在高内存压力下可能会被不公平地杀死。(特别是如果数据集足够大以至于需要进行比原先启用的交换量大得多的交换,则会导致较高的交换风暴风险,并且后期OOM杀手会太强。)

解决方案,或者至少是可行的方法,是对关键部分(甚至是整个应用程序/服务,如果它适用于不应交换到磁盘的敏感数据)进行内存锁定,或者将内存映射用于专用的备份文件。(对于后者,是我在2011年编写的示例,该示例操作了TB级的数据集。)

OOM杀手仍然可以终止进程,并且仍然会发生SIGSEGV(由于内核无法向其提供RAM支持的库函数内部分配),除非所有应用程序都被锁定到RAM,但至少服务/流程不再是不
公平的 目标,只是因为它使用了大量内存。

可能会捕获到SIGSEGV信号(在没有可用内存来备份虚拟内存时发生),但是到目前为止,我还没有看到可以保证代码复杂性和所需维护工作的用例。

总之,对所述问题的正确答案是“ 否”,不要这样做

2020-06-03