我是否在不更改新创建的数组的值时遇到请求分页?

Am I experiencing demand paging when not altering the values of a newly created array?

我正在学习操作系统的内存管理,刚刚学习了虚拟内存以及如何使用按需分页

我做了这个简单的程序:

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

int main(void){
  int x=0;
  scanf("%d",&x);
  if(x==1){
    int *a=malloc(1073741824*sizeof(int));
    while(1){
      for(size_t i=0;i<1073741824;i++){
        a[i]=1;
      }
    }
  }
  else if(x==2){
    int *b=malloc(1073741824*sizeof(int));
    while(1);
  }
  return 0;
}

它有 2 个路径:

  1. 一个分配了一个4 gb的数组并不断改变它的值。

  2. 另一个分配了一个4 gb的数组,但没有改变它的值。

正如我所料,在运行第一个选项之后,程序的内存增加到大约4 gb,但第二个没有变化。我猜这是由于内存未被访问,因此它的页面被“换出”到后备存储,直到再次需要它们。

我的理解正确吗?

如果启用代码优化,生成的代码根本不会调用 malloc

.LC0:
        .string "%d"
main:
        sub     rsp, 24
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        lea     rsi, [rsp+12]
        mov     DWORD PTR [rsp+12], 0
        call    __isoc99_scanf
        mov     eax, DWORD PTR [rsp+12]
        cmp     eax, 1
        je      .L4
        cmp     eax, 2
        je      .L6
        xor     eax, eax
        add     rsp, 24
        ret
.L4:
        mov     eax, 1073741824
.L3:
        sub     rax, 1
        jne     .L3
        jmp     .L4
.L6:
        jmp     .L6

为了防止它使指针易变:

int main(void){
  int x=0;
  scanf("%d",&x);
  if(x==1){
    int * volatile a=malloc(1073741824*sizeof(int));
    while(1){
      for(size_t i=0;i<1073741824;i++){
        a[i]=1;
      }
    }
  }
  else if(x==2){
    int * volatile b=malloc(1073741824*sizeof(int));
    while(1);
  }
  return 0;
}

现在两种情况下都应该 malloc:

.LC0:
        .string "%d"
main:
        sub     rsp, 24
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        lea     rsi, [rsp+4]
        mov     DWORD PTR [rsp+4], 0
        call    __isoc99_scanf
        mov     eax, DWORD PTR [rsp+4]
        cmp     eax, 1
        je      .L10
        cmp     eax, 2
        je      .L11
        xor     eax, eax
        add     rsp, 24
        ret
.L10:
        mov     edi, 1
        sal     rdi, 32
        call    malloc
        mov     QWORD PTR [rsp+8], rax
.L4:
        mov     eax, 1073741824
.L3:
        mov     rdx, QWORD PTR [rsp+8]
        sub     rax, 1
        jne     .L3
        jmp     .L4
.L11:
        mov     edi, 1
        sal     rdi, 32
        call    malloc
        mov     QWORD PTR [rsp+8], rax
.L6:
        jmp     .L6

请求分页是指当您访问不在 Linux 的页面缓存中的内存页面时。例如,如果您使用 memory mapping you can access a file on disk as if it were in RAM. When you dereference a memory mapped pointer Linux first checks the page cache for the data; if the page isn't in the cache then it page faults 并从磁盘加载缺失的页面。您的程序看不到所有在幕后进行的按需磁盘访问。

请求分页与新的 malloc() 调用无关,因为磁盘上没有任何内容。你看到的是 overcommit。 Linux 在调用 malloc() 时不分配内存;它位于并且总是 returns 一个指针是否可以满足请求。内存仅在(如果)您访问它时才实际分配。如果您要求 4GB 内存但不对其进行任何操作,则实际上没有分配任何内存。

毫不奇怪,撒谎并不总是奏效。 Linux 可能实际上无法满足程序的内存需求,但程序无法知道,因为 malloc() 没有 return NULL。这就像航空公司超额预订航班一样。航空公司给你一张机票,但当你出现在机场时,他们告诉你他们 运行 没有座位并将你赶下飞机。您尝试访问您分配的内存和 Linux 恐慌,调用 OOM killer, and starts killing processes to free up memory. Who does it kill? Maybe your process. Maybe you're spared and others die. Suffice it to say, overcommit is a controversial feature.