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

Cab*_*age 5 c malloc ps virtual-memory 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();
}
Run Code Online (Sandbox Code Playgroud)

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

在进行 malloc 之前: 在malloc之前

malloc 之后: malloc之后

有两件事我不明白:

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

请帮忙并谢谢。

Ant*_*ala 8

他们两个的答案是相同的,而且非常简单。

您可能知道,Glibcmalloc将用于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);
}
Run Code Online (Sandbox Code Playgroud)

输出类似的东西

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
Run Code Online (Sandbox Code Playgroud)

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

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

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


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