c - 为什么这个内存吞噬者真的不吃内存?

我想创建一个程序来模拟 Unix 服务器上的内存不足 (OOM) 情况。我创建了这个 super 简单的内存吞噬者:

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

unsigned long long memory_to_eat = 1024 * 50000;
size_t eaten_memory = 0;
void *memory = NULL;

int eat_kilobyte()
{
    memory = realloc(memory, (eaten_memory * 1024) + 1024);
    if (memory == NULL)
    {
        // realloc failed here - we probably can't allocate more memory for whatever reason
        return 1;
    }
    else
    {
        eaten_memory++;
        return 0;
    }
}

int main(int argc, char **argv)
{
    printf("I will try to eat %i kb of ram\n", memory_to_eat);
    int megabyte = 0;
    while (memory_to_eat > 0)
    {
        memory_to_eat--;
        if (eat_kilobyte())
        {
            printf("Failed to allocate more memory! Stucked at %i kb :(\n", eaten_memory);
            return 200;
        }
        if (megabyte++ >= 1024)
        {
            printf("Eaten 1 MB of ram\n");
            megabyte = 0;
        }
    }
    printf("Successfully eaten requested memory!\n");
    free(memory);
    return 0;
}

它消耗的内存与 memory_to_eat 中定义的一样多,现在正好是 50 GB 的 RAM。它按 1 MB 分配内存,并准确打印无法分配更多内存的点,以便我知道它设法吃掉了哪个最大值。

问题是它有效。即使在具有 1 GB 物理内存的系统上也是如此。

当我查看顶部时,我发现该进程占用了 50 GB 的虚拟内存,而驻留内存仅不到 1 MB。有没有办法创建一个真正消耗它的内存吞噬者?

系统规范:Linux 内核 3.16 (Debian) 很可能启用了过量使用(不确定如何检查),没有交换和虚拟化。

最佳答案

当您的 malloc() 实现向系统内核请求内存时(通过 sbrk()mmap() 系统调用),内核仅记录您已请求内存以及将其放置在地址空间中的位置。 它实际上还没有映射这些页面

当进程随后访问新区域内的内存时,硬件会识别出段错误并向内核发出警报。然后内核在它自己的数据结构中查找页面,并发现那里应该有一个零页面,所以它映射到一个零页面(可能首先从页面缓存中逐出一个页面)并从中断返回。您的进程没有意识到发生了任何这种情况,内核操作是完全透明的(除了内核工作时的短暂延迟)。

此优化允许系统调用非常快速地返回,并且最重要的是,它避免了在进行映射时将任何资源提交给您的进程。这允许进程保留在正常情况下它们永远不需要的相当大的缓冲区,而不必担心会占用过多的内存。


所以,如果你想编写一个内存吞噬者,你绝对必须对你分配的内存做一些实际的事情。为此,您只需在代码中添加一行:

int eat_kilobyte()
{
    if (memory == NULL)
        memory = malloc(1024);
    else
        memory = realloc(memory, (eaten_memory * 1024) + 1024);
    if (memory == NULL)
    {
        return 1;
    }
    else
    {
        //Force the kernel to map the containing memory page.
        ((char*)memory)[1024*eaten_memory] = 42;

        eaten_memory++;
        return 0;
    }
}

请注意,在每个页面中写入单个字节就足够了(在 X86 上包含 4096 个字节)。这是因为从内核到进程的所有内存分配都是以内存页面粒度完成的,这反过来又是因为硬件不允许以更小的粒度进行分页。

https://stackoverflow.com/questions/33234139/

相关文章:

python - 使用 Python 进行网页抓取

linux - 在 Linux 上的目录中获取最新文件

python - 如何将 apply() 函数用于单个列?

python - 如果对象存在,我如何获取它,或者如果它在 Django 中不存在,我如何获取它?

linux - Bash中的 'eval'命令及其典型用途

linux - 单个主机上的多个 glibc 库

python - 将 Python argparse.Namespace() 视为字典的正确方法是什

python - 从标准输入读取密码

python - 用 Python 构建一个最小的插件架构

linux - "/usr/bin/ld: cannot find -lz"