Испытываю ли я подкачку по запросу, когда не изменяю значения только что созданного массива?

Я изучаю управление памятью ОС и только что узнал о виртуальной памяти и о том, как ее можно реализовать с помощью подкачки по запросу .

Я сделал эту простую программу:

      #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;
}

Она имеет пути:

  1. Один выделил массив и продолжает изменять его значения.

  2. Другой выделил массив, но не меняет его значений.

Как я и ожидал, после запуска первого варианта память программы увеличивается примерно до , но второй не меняется. Я предполагаю, что это связано с тем, что к памяти не осуществляется доступ, поэтому ее страницы «выгружаются» в резервное хранилище, пока они снова не понадобятся.

Я правильно понимаю?

2 ответа

Решение

Пейджинг по запросу - это когда вы обращаетесь к страницам памяти, которых нет в кэше страниц Linux. Например, если вы используете отображение памяти, вы можете получить доступ к файлу на диске, как если бы он находился в ОЗУ. Когда вы разыменовываете указатель с отображением в память, Linux сначала проверяет кеш страницы на наличие данных; если страницы нет в кеше, происходит сбой страницы и загружаются недостающие страницы с диска. Ваша программа не видит весь доступ к диску по запросу, происходящий за кулисами.

Пейджинг по запросу не имеет отношения к новому вызову, поскольку на диске ничего нет. То, что вы видите, называется чрезмерной приверженностью. Linux не выделяет память при вызове; он лежит и всегда возвращает указатель независимо от того, может он удовлетворить запрос или нет. Память фактически выделяется только тогда, когда (если) вы обращаетесь к ней. Если вы просите 4 ГБ памяти, но ничего не делаете с ними, на самом деле ничего не выделяется.

Неудивительно, что ложь не всегда получается. Linux может фактически не удовлетворить потребности программы в памяти, но программа не может знать, потому что malloc()не вернул NULL. Это похоже на авиакомпанию, которая забронирует больше рейсов. Авиакомпания выдает вам билет, но когда вы появляетесь в аэропорту, они говорят вам, что у них закончились места, и выгоняют вас с рейса. Вы пытаетесь получить доступ к выделенной вами памяти, и Linux паникует, вызывает OOM-убийцу и начинает убивать процессы, чтобы освободить память. Кого это убивает? Может ваш процесс. Может быть, тебя пощадили, а другие умрут. Достаточно сказать, что чрезмерная приверженность - спорная особенность.

Если вы включите оптимизацию кода, сгенерированный код не будет вызывать 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
Другие вопросы по тегам