malloc是否懒惰地为Linux(和其他平台)上的分配创建后备页?

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

我确定它会为分配分配一个虚拟地址(如果需要,可以通过走空闲列表并创建一个新映射),但是它确实创建了1 GiB的交换页面吗? 或者当它实际上像mmap那样触摸它们时,它会mprotect地址范围并创建页面?

(我指定了Linux,因为标准对这些细节没有提及,但我很想知道其他平台也能做什么。)


Linux确实推迟了页面分配,也就是说。 '乐观的内存分配'。 你从malloc中获得的内存不会被任何东西所支持,当你触摸它的时候,你实际上可能会得到一个OOM条件(如果没有你请求的页面的交换空间),在这种情况下,一个进程会被毫不客气地终止。

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


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 MiBn", n);
                return 0;
        }
        printf ("got %d MiBn", ++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 MiBn", n);
                return 0;
        }
        memset (p, 0, (1<<20));
        printf ("got %d MiBn", ++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 MiBn", n);

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

    return 0;
}

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


我在同一主题上给出了类似帖子的答案:

有些分配器是懒惰的吗?

这开始有点偏离主题(然后我会将它与你的问题联系起来),但是发生的情况与在Linux中分离进程时发生的情况类似。 分叉时,有一种称为“拷贝写入”的机制,它只在内存写入时才复制新进程的内存空间。 这样,如果分支进程exec立即执行一个新程序,那么您已经节省了复制原始程序内存的开销。

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

这是什么目的? 它基本上使mallocing内存的操作大致是一个大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万英镑,但不对他们做任何事情。 deadbeef向每个页面写入一个int,导致19531个写入,并且刚刚写入19531个int并将它们清零。 正如你所看到的,deadbeef执行的时间比bigmalloc大100倍,比justwriter大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;
}
链接地址: http://www.djcxy.com/p/28445.html

上一篇: Does malloc lazily create the backing pages for an allocation on Linux (and other platforms)?

下一篇: C++ like vs C like casts?