首页 > 解决方案 > 在 32 位 Linux 中无法耗尽物理内存

问题描述

所以我有一个有趣的基于操作系统的问题给你。在过去的几个小时里,我一直在与我认识的任何有 C 编程经验的人交谈,但似乎没有人能够就为什么会发生这种行为给出明确的答案。

我有一个故意设计为导致极端内存泄漏的程序(例如,当您在分配内存后不释放内存时会发生什么)。在 64 位操作系统(Windows、Linux 等)上,它会做它应该做的事情。它填充物理内存,然后填充操作系统的交换空间。在 Linux 中,该进程随后被操作系统终止。然而,在 Windows 中,它不是,它会继续运行。最终结果是系统崩溃。

这是代码:

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

void main()
{
    while(1)
    {
        int *a;
        a = (int*)calloc(65536, 4);
    }
}

但是,如果您在 32 位 Linux 发行版上编译并运行此代码,则它对物理内存的使用完全没有影响。它使用了我分配的 4 GB RAM 的大约 1%,之后就再也没有增加。我没有要测试的 32 位 Windows 的合法副本,所以我不能确定这也发生在 32 位 Windows 上。

有人可以解释为什么使用 calloc 会填充 64 位 Linux 操作系统的物理内存,而不是 32 位 Linux 操作系统吗?

标签: cmemory-leaksoperating-system

解决方案


mallocand函数在calloc技术上并不分配内存,尽管它们的名字。它们实际上为您的程序地址空间的一部分分配了操作系统级别的读/写权限。这是一个微妙的差异,大多数时候并不相关。

正如所写的那样,这个程序只消耗地址空间。最终,calloc将开始返回 NULL 但程序将继续运行。

#include <stdlib.h>
// Note main should be int.
int main() {
    while (1) {
        // Note calloc should not be cast.
        int *a = calloc(65536, sizeof(int));
    }
}

如果您写入从 calloc 返回的地址,它将强制内核分配内存来支持这些地址。

#include <stdlib.h>
#include <string.h>
int main() {
    size_t size = 65536 * 4;
    while (1) {
        // Allocates address space.
        void *p = calloc(size, 1);
        // Forces the address space to have allocated memory behind it.
        memset(p, 0, size);
    }
}

仅写入返回的块中的单个位置是不够的,calloc因为分配实际内存的粒度是 4 KiB(页面大小...... 4 KiB 是最常见的)。因此,您只需写入每一页即可。

64位的情况呢?

分配地址空间有一些记账开销。在 64 位系统上,您会获得 40 或 48 位的地址空间,其中大约一半可以分配给程序,即至少 8 TiB。在 32 位系统上,这达到 2 GiB 左右(取决于内核配置)。

因此,在 64 位系统上,您可以分配 ~8 TiB,而在 32 位系统上,您可以分配 ~2 GiB,而开销是导致问题的原因。malloc对或的每次调用通常都会产生少量开销calloc

另请参阅为什么 malloc+memset 比 calloc 慢?


推荐阅读