malloc 是否会延迟为 Linux(和其他平台)上的分配创建支持页面?

2024-10-22 08:28:00
admin
原创
281
摘要:问题描述:如果我在 Linux 上malloc(1024 * 1024 * 1024),malloc 实际上做什么?我确信它会为分配分配一个虚拟地址(通过遍历空闲列表并在必要时创建新映射),但它是否真的创建了 1 GiB 的交换页面?还是它会mprotect像你实际接触它们时一样确定地址范围并创建页面mmap...

问题描述:

如果我在 Linux 上malloc(1024 * 1024 * 1024),malloc 实际上做什么?

我确信它会为分配分配一个虚拟地址(通过遍历空闲列表并在必要时创建新映射),但它是否真的创建了 1 GiB 的交换页面?还是它会mprotect像你实际接触它们时一样确定地址范围并创建页面mmap

(我指定 Linux 是因为标准没有提及这些细节,但我也很想知道其他平台是如何做的。)


解决方案 1:

Linux 会延迟页面分配,又称为“乐观内存分配”。您从 malloc 获得的内存没有任何支持,当您触碰它时,您实际上可能会遇到 OOM 情况(如果您请求的页面没有交换空间),在这种情况下,进程会被毫不客气地终止。

例如参见http://www.linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.html

解决方案 2:

9. 内存Linux 内核的一部分, Andries Brouwer 撰写的关于 Linux 内核的一些评论)是一份很好的文档。

它包含以下程序,演示 Linux 对物理内存与实际内存的处理,并解释内核的内部结构。

通常,第一个演示程序会在 malloc() 返回 NULL 之前获得大量内存。第二个演示程序会获得少得多的内存,因为之前获得的内存实际上已被使用。第三个程序会获得与第一个程序相同的大量内存,然后在需要使用内存时被终止。

演示程序1:分配内存但不使用它。

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

int main (void) {
    int n = 0;

    while (1) {
        if (malloc(1<<20) == NULL) {
                printf("malloc failure after %d MiB
", n);
                return 0;
        }
        printf ("got %d MiB
", ++n);
    }
}

演示程序 2:分配内存并实际触碰所有内存。

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

int main (void) {
    int n = 0;
    char *p;

    while (1) {
        if ((p = malloc(1<<20)) == NULL) {
                printf("malloc failure after %d MiB
", n);
                return 0;
        }
        memset (p, 0, (1<<20));
        printf ("got %d MiB
", ++n);
    }
}

演示程序3:先分配,后使用。

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

#define N       10000

int main (void) {
    int i, n = 0;
    char *pp[N];

    for (n = 0; n < N; n++) {
        pp[n] = malloc(1<<20);
        if (pp[n] == NULL)
            break;
    }
    printf("malloc failure after %d MiB
", n);

    for (i = 0; i < n; i++) {
        memset (pp[i], 0, (1<<20));
        printf("%d
", i+1);
    }

    return 0;
}

(在Solaris等运行良好的系统上,这三个演示程序获得相同数量的内存且不会崩溃,但 malloc() 返回 NULL。)

解决方案 3:

我针对同一主题的类似帖子给出了以下回答:

一些分配器是懒惰的吗?

这开始有点偏离主题(然后我会把它和你的问题联系起来),但发生的事情类似于在 Linux 中 fork 进程时发生的事情。fork 时有一种称为写时复制的机制,它只在内存被写入时复制新进程的内存空间。这样,如果 fork 进程立即执行一个新程序,那么你就节省了复制原始程序内存的开销。

回到你的问题,这个想法是相似的。正如其他人指出的那样,请求内存会立即获得虚拟内存空间,但实际页面仅在写入时才分配。

这样做的目的是什么?它基本上使 mallocing 内存成为一个或多或少恒定时间操作 Big O(1),而不是 Big O(n) 操作(类似于 Linux 调度程序将其工作分散开来而不是将其集中在一个大块中的方式)。

为了证明我的意思我做了以下实验:

rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc

real    0m0.005s
user    0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef

real    0m0.558s
user    0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites

real    0m0.006s
user    0m0.000s
sys 0m0.008s

bigmalloc 程序分配了 2000 万个 int,但并未对它们执行任何操作。deadbeef 将一个 int 写入每个页面,导致写入次数为 19531 次,而 justwrites 分配了 19531 个 int 并将其清零。如您所见,deadbeef 的执行时间比 bigmalloc 长约 100 倍,比 justwrites 长约 50 倍。

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes

    return 0;
}

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = malloc(sizeof(int)*20000000); // Allocate 80 million bytes

    // Immediately write to each page to simulate an all-at-once allocation
    // assuming 4k page size on a 32-bit machine.

    for (int* end = big + 20000000; big < end; big += 1024)
        *big = 0xDEADBEEF;

    return 0;
}

#include <stdlib.h>

int main(int argc, char **argv) {

    int *big = calloc(sizeof(int), 19531); // Number of writes

    return 0;
}

解决方案 4:

Malloc 从 libc 管理的块中分配内存。当需要额外内存时,库会使用 brk 系统调用进入内核。

内核将虚拟内存页面分配给调用进程。这些页面作为进程拥有的资源的一部分进行管理。内存被 brk 时不会分配物理页面。当进程访问被 brk 的页面中的任何内存位置时,就会发生页面错误。内核验证虚拟内存是否已分配,然后继续将物理页面映射到虚拟页面。

页面分配不仅限于写入,与写入时复制截然不同。任何访问(读取或写入)都会导致页面错误和物理页面映射。

请注意,堆栈内存是自动映射的。也就是说,不需要显式 brk 将页面映射到堆栈使用的虚拟内存。

解决方案 5:

在 Windows 上,页面已被提交(即可用的空闲内存减少),但是直到您接触这些页面(读取或写入)时,它们才会真正被分配。

解决方案 6:

在大多数类 Unix 系统中,它管理brk边界。当处理器命中时,VM 会添加页面。至少 Linux 和BSD是这样做的。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用