为什么 malloc() 会导致轻微的页面错误?

Why does malloc() cause minor page fault?

我想了解内存和页面错误,所以我写了下面的代码来检查我的理解。我不明白为什么调用 malloc 会导致 MINFL 增加,因为 malloc() 不应该影响物理内存(据我所知)。

这是我的代码:

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

void main() {
  printf("Before malloc\n");
  getchar();
  
  malloc(1 << 20);
  printf("After malloc\n");
  getchar();
}

这些是 ps 命令的最终结果。

在 malloc 之前:

malloc 之后:

有两点我不明白:

  1. 为什么 MINFL 会增加?
  2. 为什么 VSZ 增加了 1028 而不是 1024?

请帮忙,谢谢。

两个的答案是一样的,确实很简单。

如您所知,Glibc malloc 将使用 mmap 直接分配大于 128 KiB 的块。但是,它将需要在指针下方 写入簿记信息 - 因为如果只给一个指针,free 还怎么知道它应该做什么。如果打印 malloc 返回的指针,您将看到它 页对齐。

这是一个演示所有这些的程序:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>

#define ALLOCATION_SIZE (1 << 20)

int main(void) {
    struct rusage usage = {0};

    getrusage(RUSAGE_SELF, &usage);
    printf("1st before malloc: %lu\n", usage.ru_minflt);

    getrusage(RUSAGE_SELF, &usage);
    printf("2nd before malloc: %lu\n", usage.ru_minflt);

    char *p = malloc(ALLOCATION_SIZE);
    printf("pointer returned from malloc: %p\n", p);

    getrusage(RUSAGE_SELF, &usage);
    printf("after malloc: %lu\n", usage.ru_minflt);

    p[0] = 42;
    getrusage(RUSAGE_SELF, &usage);
    printf("after writing to the beginning of the allocation: %lu\n", usage.ru_minflt);

    for (size_t i = 0; i < ALLOCATION_SIZE; i++) {
        p[i] = 42;
    }
    getrusage(RUSAGE_SELF, &usage);
    printf("after writing to every byte of the allocation: %lu\n", usage.ru_minflt);
}

输出类似于

1st before malloc: 108
2nd before malloc: 118
pointer returned from malloc: 0x7fbcb32aa010
after malloc: 119
after writing to the beginning of the allocation: 119
after writing to every byte of the allocation: 375

getrusageprintf 第一次导致页面错误,所以我们调用它两次 - 现在错误计数在 malloc 调用之前是 118,在 malloc 之后是 119 . 如果你看一下指针,0x010 不是 0x000 即分配不是页面对齐的 - 前 16 个字节包含 free 的簿记信息,因此它知道它需要使用 munmap 来释放内存块,以及分配块的大小!

现在这自然就解释了为什么大小增加是 1028 Ki 而不是 1024 Ki - 必须保留一个额外的页面 以便有足够的 space那16个字节!它还解释了页面错误的来源——因为 malloc 必须将簿记信息写入写时复制归零页面。这可以通过写入分配的第一个字节来证明——它不再导致页面错误。

最后,for 循环将修改页面并触及 257 映射中剩余的 256 页。


如果您将 ALLOCATION_SIZE 更改为 ((1 << 20) - 16),即只分配少 16 个字节,您会发现虚拟大小和页面错误数都与值 在期待。