【问题标题】:infinite loop malloc in a 32 bit kernel with 4 Gb RAM and 10 Gb Swap Partition具有 4 Gb RAM 和 10 Gb 交换分区的 32 位内核中的无限循环 malloc
【发布时间】:2017-01-27 11:50:38
【问题描述】:

假设我有一个 32 位内核。 4 Gb RAM,10 Gb 交换分区。

我有一个进程在无限循环中使用 malloc。因此,最终系统的OOM会杀死该进程。这里有两个论点。

参数 1:因为它是 32 位内核,虚拟地址拆分为 3:1,即用户空间为 3Gb,内核空间为 1Gb。该进程将被分配 3 Gb 的内存,然后就没有更多的内存可以提供了。因此,OOM 会终止该进程。

参数 2:因为它是 32 位内核,虚拟地址拆分为 3:1,即用户空间为 3Gb,内核空间为 1Gb。该进程将被分配 4 Gb 的内存,然后就没有更多的内存可以提供了。因此,OOM 会终止该进程。

参数 3:Malloc 将首先占用 4 Gb 内存的 Ram,然后占用 10 Gb 的 Swap 分区,然后 OOM 将终止该进程。

这些论点(如果有的话)中哪些是正确的?

论坛上有其他答案,但我不确定这个问题的答案是否取决于它的 32 位内核还是 64 位内核?

【问题讨论】:

  • 没有。您的系统实际上有 14gig 的内存。但是在 32 位系统上,没有一个进程可以分配超过 4gig 的内存,而不管实际发生的 1:3 拆分内容如何。从技术上讲,交换文件根本不会被触及,假设 4gig ram 完全是空的。
  • 假设一个进程的虚拟内存(高内存区域)的 1 gig 是为内核操作保留的。用户空间可以使用剩余的 3 gig。这是一个 3/1 的拆分。 malloc 不应该只占用 3 Gb 的用户空间吗?因为它是用户级进程。
  • @saurabhagarwal, no 虚拟内存是为内核操作保留的。内核以实模式运行。或者换句话说,内核的内存不占用分配给任何进程的虚拟内存的任何部分。每个进程的虚拟内存空间都属于它,而且是它自己的。
  • 好吧,现在说得通了。感谢您指出。但是内核用户分割虚拟地址空间的含义是什么?这种分裂有什么意义?
  • malloc 可能会失败,也可能会过度提交,或两者兼而有之。没有通用且可靠的方法来使用 malloc 触发 OOM 处理程序。

标签: c linux memory-management linux-kernel operating-system


【解决方案1】:

为什么认为OOM会杀死进程?在您的示例中,您将在用完所有真实内存之前耗尽您的地址空间。因此,在您的示例(32 位)中,您将能够分配大约 3 GB 的地址空间(减去文本/数据/堆栈和其他段以及可能的内存碎片),然后系统将只返回 ENOMEM。

如果您使用的是 64 位系统,事情会变得更有趣。现在结果将在很大程度上取决于您是否真的在使用此内存。如果您不使用它(只是分配),那么由于过度使用(当然,如果您没有禁用它)您将能够分配大量的地址空间,但是如果您尝试使用它,这就是(大约 10+4 GB 边界)您将触发 OOM 并杀死程序的地方。

更新

用这样的程序检查实际上很容易:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
    static char *allocs[10 * 1024 * 1024];
    static unsigned int size;
    unsigned int i;
    unsigned int const chunk = 1024 * 1024;

    for (i = 0; i < (sizeof(allocs)/sizeof(allocs[0])); i++)
        if ((allocs[i] = malloc(chunk)) == NULL) {
            printf("failed to allocate after %u MB: %s\n", i, strerror(errno));
            break;
        }

    size = i;
    if (size == (sizeof(allocs)/sizeof(allocs[0])))
         printf("allocated %u MB successfully!\n", size);
    for (i = 0; i < size; i++) {
        memset(allocs[i], 1, chunk);
        if (i % 100 == 0)
            printf("memset %u MB\n", i);
    }
    return 0;
}

它尝试分配 10M 的内存块,每个 1M 的大小,因此实际上是 10 TB。在具有 4 GB RAM 的 32 位系统上,它给出了以下结果(缩短了一点):

failed to allocate after 3016 MB: Cannot allocate memory
memset 0 MB
memset 100 MB
...
memset 2900 MB
memset 3000 MB

正如预期的那样,地址空间不足,添加交换空间(我用 5GB 而不是 10GB 完成了我的测试)并没有任何帮助。

对于 64 位,它的行为有点出人意料(这没有交换,只有 4 GB 的 RAM),因为它不输出任何内容,并且在仍在进行分配时被 OOM 杀手杀死。但这可以通过查看 OOM 杀手日志来解释:

[  242.827042] [ 5171]  1000  5171 156707933   612960  306023        0             0 a.out
[  242.827044] Out of memory: Kill process 5171 (a.out) score 905 or sacrifice child
[  242.827046] Killed process 5171 (a.out) total-vm:626831732kB, anon-rss:2451812kB, file-rss:28kB

所以它能够分配大约 620 GB (!) 的虚拟内存,真正映射了 2.4。还请记住,我们使用malloc() 进行分配,因此 C 库必须自己进行内部管理,即使您估计在如此大的分配情况下它的开销约为 0.5%,您也会为此获得大量数字(免费您可以尝试使用mmap())。在这个阶段,我们有足够的内存来使用它,所以进程在仍然进行分配的同时被杀死。如果你想让它不那么贪婪并将 allocs 更改为[100 * 1024](实际上只有 100 GB),你可以很容易地看到交换的效果,因为在 4 GB 系统上没有它你有:

allocated 102400 MB successfully!
memset 0 MB
memset 100 MB
...
memset 2800 MB
memset 2900 MB
Killed

并添加了 5 GB 的交换空间:

allocated 102400 MB successfully!
memset 0 MB
memset 100 MB
...
memset 7900 MB
memset 8000 MB
Killed

一切如预期。

【讨论】:

    【解决方案2】:

    让我们先稍微澄清一下您的要求。您想要运行 32 位进程。它在一个紧密的循环中分配内存并改变它。最后一部分是必要的,否则只会保留地址空间,但不会向其提交内存。

    您的程序将消耗最多 32 位虚拟地址空间的可用部分的资源。对于 x86 上的大多数 32 位类 UNIX 内核,这将是 3GB,因为顶部的 1GB 是为内核本身保留的。 Linux 有一个构建时间选项可以将其更改为 2GB:2GB 拆分。如果你在 Xen 下运行,内核使用一个单独的地址空间,所以你的程序实际上可以消耗全部 4GB。其他 CPU 架构通常介于这两种情况之间。

    现在我们已经确定程序将使用 3GB 到 4GB 的内存,让我们看看内核可用的资源。如果你有 10GB 的交换空间,它总是可以将足够的页面推入交换空间以保持程序运行。假设没有其他程序在运行大分配,您将永远不会遇到 OOM 杀手,因为您并没有首先耗尽内存。

    关于 OOM 杀手的最后一句话。如果您不更改分配的内存并且运行程序的多个实例(例如 > 5),它们仍然不会消耗超过几 MB 的实际内存(RAM +交换)。这就是所谓的内存过量使用。现在,如果程序开始更改分配,它们将慢慢减少可用的 RAM+swap 并在某个时候耗尽。这是OOM杀手触发的情况。如果没有内存过量使用,只要所有正在运行的进程的大小总和大于 RAM+swap,malloc 就会返回错误。由于某些类型的内存映射不计算在内,因此略微简化了这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-12
      • 2013-02-27
      • 2010-11-10
      • 1970-01-01
      • 2013-11-27
      • 2015-11-09
      • 1970-01-01
      • 2012-04-04
      相关资源
      最近更新 更多