代码之家  ›  专栏  ›  技术社区  ›  Aaron Maenpaa

malloc是否在Linux(和其他平台)上为分配惰性地创建备份页?

  •  63
  • Aaron Maenpaa  · 技术社区  · 15 年前

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

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

    the standard

    6 回复  |  直到 15 年前
        1
  •  43
  •   Remus Rusanu    15 年前

    Linux执行延迟页面分配,也称为“延迟页面分配”乐观内存分配'。从malloc返回的内存没有任何支持,当您触摸它时,实际上可能会得到一个OOM条件(如果您请求的页面没有交换空间),在这种情况下 a process is unceremoniously terminated .

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

        2
  •  16
  •   Peter Mortensen icecrime    10 年前

    9. Memory (部分 The Linux kernel , Some remarks on the Linux Kernel 安德里斯·布劳沃(Andries Brouwer)是一份很好的文件。

    它包含以下程序,演示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", n);
                    return 0;
            }
            printf ("got %d MiB\n", ++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", n);
                    return 0;
            }
            memset (p, 0, (1<<20));
            printf ("got %d MiB\n", ++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", n);
    
        for (i = 0; i < n; i++) {
            memset (pp[i], 0, (1<<20));
            printf("%d\n", i+1);
        }
    
        return 0;
    }
    

    (在运行良好的系统上,如 Solaris

        3
  •  11
  •   Community CDub    7 年前

    Are some allocators lazy?

    这开始有点离题(然后我会把它和你的问题联系起来),但发生的情况与在Linux中分叉进程时发生的情况类似。分叉时,有一种称为写时复制的机制,当内存也被写入时,它只为新进程复制内存空间。这样,如果forked process exec立即是一个新程序,那么您就节省了复制原始程序内存的开销。

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

    这样做的目的是什么?它基本上使mallocing内存或多或少成为一个常量时间操作大O(1)而不是大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
    

    #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
  •  6
  •   IAPAddler IAPAddler    15 年前

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

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

    页面分配不仅限于写操作,而且与写时复制完全不同。任何访问(读或写)都会导致页面错误和物理页面的映射。

        5
  •  5
  •   Peter Mortensen icecrime    10 年前

    在Windows上,页面已提交(即可用的可用内存下降),但在触摸页面(读或写)之前,不会实际分配页面。

        6
  •  2
  •   Peter Mortensen icecrime    10 年前

    在大多数类Unix系统上,它管理 brk BSDs