32位内核中的无限循环malloc,具有4 Gb RAM和10 Gb交换分区

32位内核中的无限循环malloc,具有4 Gb RAM和10 Gb交换分区,c,linux,memory-management,linux-kernel,operating-system,C,Linux,Memory Management,Linux Kernel,Operating System,假设我有一个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会扼杀这

假设我有一个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首先占用4GB内存,然后占用10GB交换分区,然后OOM会终止进程

以下哪一个论点(如果有的话)是正确的


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

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

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

更新 实际上,使用这样的程序很容易检查:

#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;
}
正如预期的那样,地址空间不足,添加交换(我用5GB而不是10 GB进行了测试)在任何方面都没有帮助

对于64位,它的行为有点出乎意料(这是没有交换的,只有4GB的RAM),因为它不输出任何东西,只是在执行分配时被OOM killer杀死。但这是可以解释的,看看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
因此,它能够分配大约620GB(!)的虚拟内存,真正映射到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

一切如预期。

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

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

更新 实际上,使用这样的程序很容易检查:

#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;
}
正如预期的那样,地址空间不足,添加交换(我用5GB而不是10 GB进行了测试)在任何方面都没有帮助

对于64位,它的行为有点出乎意料(这是没有交换的,只有4GB的RAM),因为它不输出任何东西,只是在执行分配时被OOM killer杀死。但这是可以解释的,看看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
因此,它能够分配大约620GB(!)的虚拟内存,真正映射到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

一切如预期。

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

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

现在我们已经确定,该计划将