Потрібно пояснення щодо розміру постійного набору / віртуального розміру


61

Я виявив, що pidstatце буде хорошим інструментом для моніторингу процесів. Я хочу обчислити середнє використання пам'яті для певного процесу. Ось декілька прикладів виводу:

02:34:36 PM       PID  minflt/s  majflt/s     VSZ    RSS   %MEM  Command
02:34:37 PM      7276      2.00      0.00  349212 210176   7.14  scalpel

(Це частина результату з pidstat -r -p 7276.)

Чи слід використовувати інформацію про розмір постійного набору (RSS) або віртуальний розмір (VSZ) для обчислення середнього споживання пам'яті? Я прочитав кілька речей у Вікіпедії та на форумах, але не впевнений, що повністю розумію відмінності. Плюс, здається, що жоден з них не є надійним. Отже, як я можу відстежувати процес, щоб отримати його пам'ять?

Будь-яка допомога з цього приводу буде корисною.



Відповіді:


63

RSS - скільки пам’яті цього процесу в основній пам'яті (ОЗП). VSZ - скільки віртуальної пам’яті має процес. Сюди входять всі типи пам’яті, як в оперативній пам’яті, так і заміненій. Ці числа можуть перекручуватися, оскільки вони також включають спільні бібліотеки та інші типи пам'яті. Ви можете мати п'ять сотень екземплярів bashзапуску, і загальний розмір стопи пам’яті не буде сумою їх значень RSS або VSZ.

Якщо вам потрібно отримати більш детальне уявлення про слід пам'яті процесу, у вас є кілька варіантів. Ви можете пройти /proc/$PID/mapі відпалити речі, які вам не подобаються. Якщо це спільні бібліотеки, обчислення може бути складним залежно від ваших потреб (які, я думаю, пам’ятаю).

Якщо вам важливий лише розмір купи процесу, ви завжди можете просто проаналізувати [heap]запис у mapфайлі. Розмір, який ядро ​​виділило для купи процесу, може або не відображати точну кількість байтів, які процес попросив виділити. Існують невеликі деталі, внутрішні деталі ядра та оптимізації, які можуть це відкинути. В ідеальному світі це буде стільки, скільки потрібно вашому процесу, округлене до найближчого кратного розміру системної сторінки ( getconf PAGESIZEпідкаже, що це таке - на ПК це, мабуть, 4 096 байт).

Якщо ви хочете побачити, скільки пам'яті виділив процес , один з найкращих способів - це відмовитися від показників на стороні ядра. Натомість ви задіюєте функцію розподілу пам'яті (де) розподільної пам'яті бібліотеки C за допомогою LD_PRELOADмеханізму. Особисто я трохи зловживаю, valgrindщоб отримувати інформацію про подібні речі. (Зауважте, що для застосування інструментарію потрібно буде перезапустити процес.)

Зауважте, оскільки, можливо, ви також визначаєте час виконання, це valgrindзробить ваші програми дуже повільними (але, можливо, в межах ваших допусків).


Дуже дякую! Я збираюся дослідити різні варіанти. Ви були більш ніж корисні! :)
Flanfl

"Ви можете мати п'ятсот екземплярів запуску bash, і загальний розмір стопи пам'яті не буде сумою їх значень RSS або VSZ." Але чи буде сума їх значень RSS хорошим наближенням? Як і сума резидентного стовпця зі статистики, мені не потрібно надто надійне точне значення, але мені потрібно знати на високому рівні, скільки пам'яті використовує мій процес Java
iloveretards

3
У Ubuntu це /proc/$PID/mapsрізниця в помилках чи дистрибутивах?
dolzenko

1

Приклад мінімальної експлуатації

Щоб це мало сенс, ви повинні зрозуміти основи підкачки: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work і, зокрема, що ОС може виділяти віртуальну пам’ять за допомогою таблиць сторінок / його зберігання внутрішньої пам'яті (віртуальна пам'ять VSZ) до того, як вона фактично має резервну пам’ять на оперативній пам’яті або диску (RSS-пам'ять)

Тепер, щоб спостерігати за цим у дії, давайте створимо програму, яка:

  • виділяє більше оперативної пам'яті, ніж наша фізична пам'ять mmap
  • пише по одному байту на кожну сторінку, щоб переконатися, що кожна з цих сторінок переходить із віртуальної пам'яті лише RSS та VSZ
  • перевіряє використання пам'яті процесу одним із методів, згаданих на веб-сторінці : https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c

main.c

#define _GNU_SOURCE
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

typedef struct {
    unsigned long size,resident,share,text,lib,data,dt;
} ProcStatm;

/* https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c/7212248#7212248 */
void ProcStat_init(ProcStatm *result) {
    const char* statm_path = "/proc/self/statm";
    FILE *f = fopen(statm_path, "r");
    if(!f) {
        perror(statm_path);
        abort();
    }
    if(7 != fscanf(
        f,
        "%lu %lu %lu %lu %lu %lu %lu",
        &(result->size),
        &(result->resident),
        &(result->share),
        &(result->text),
        &(result->lib),
        &(result->data),
        &(result->dt)
    )) {
        perror(statm_path);
        abort();
    }
    fclose(f);
}

int main(int argc, char **argv) {
    ProcStatm proc_statm;
    char *base, *p;
    char system_cmd[1024];
    long page_size;
    size_t i, nbytes, print_interval, bytes_since_last_print;
    int snprintf_return;

    /* Decide how many ints to allocate. */
    if (argc < 2) {
        nbytes = 0x10000;
    } else {
        nbytes = strtoull(argv[1], NULL, 0);
    }
    if (argc < 3) {
        print_interval = 0x1000;
    } else {
        print_interval = strtoull(argv[2], NULL, 0);
    }
    page_size = sysconf(_SC_PAGESIZE);

    /* Allocate the memory. */
    base = mmap(
        NULL,
        nbytes,
        PROT_READ | PROT_WRITE,
        MAP_SHARED | MAP_ANONYMOUS,
        -1,
        0
    );
    if (base == MAP_FAILED) {
        perror("mmap");
        exit(EXIT_FAILURE);
    }

    /* Write to all the allocated pages. */
    i = 0;
    p = base;
    bytes_since_last_print = 0;
    /* Produce the ps command that lists only our VSZ and RSS. */
    snprintf_return = snprintf(
        system_cmd,
        sizeof(system_cmd),
        "ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == \"%ju\") print}'",
        (uintmax_t)getpid()
    );
    assert(snprintf_return >= 0);
    assert((size_t)snprintf_return < sizeof(system_cmd));
    bytes_since_last_print = print_interval;
    do {
        /* Modify a byte in the page. */
        *p = i;
        p += page_size;
        bytes_since_last_print += page_size;
        /* Print process memory usage every print_interval bytes.
         * We count memory using a few techniques from:
         * https://stackoverflow.com/questions/1558402/memory-usage-of-current-process-in-c */
        if (bytes_since_last_print > print_interval) {
            bytes_since_last_print -= print_interval;
            printf("extra_memory_committed %lu KiB\n", (i * page_size) / 1024);
            ProcStat_init(&proc_statm);
            /* Check /proc/self/statm */
            printf(
                "/proc/self/statm size resident %lu %lu KiB\n",
                (proc_statm.size * page_size) / 1024,
                (proc_statm.resident * page_size) / 1024
            );
            /* Check ps. */
            puts(system_cmd);
            system(system_cmd);
            puts("");
        }
        i++;
    } while (p < base + nbytes);

    /* Cleanup. */
    munmap(base, nbytes);
    return EXIT_SUCCESS;
}

GitHub вище за течією .

Складіть і запустіть:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
echo 1 | sudo tee /proc/sys/vm/overcommit_memory
sudo dmesg -c
./main.out 0x1000000000 0x200000000
echo $?
sudo dmesg

де:

  • 0x1000000000 == 64GiB: 2x фізична оперативна пам’ять мого комп’ютера 32GiB
  • 0x200000000 == 8GiB: друкуйте пам’ять кожні 8GiB, тому ми повинні отримати 4 відбитки до краху приблизно на 32GiB
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory: потрібно для Linux, щоб ми могли зробити mmap-виклик, більший за фізичну оперативну пам’ять: https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

Вихід програми:

extra_memory_committed 0 KiB
/proc/self/statm size resident 67111332 768 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 1648

extra_memory_committed 8388608 KiB
/proc/self/statm size resident 67111332 8390244 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 8390256

extra_memory_committed 16777216 KiB
/proc/self/statm size resident 67111332 16778852 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 16778864

extra_memory_committed 25165824 KiB
/proc/self/statm size resident 67111332 25167460 KiB
ps -o pid,vsz,rss | awk '{if (NR == 1 || $1 == "29827") print}'
  PID    VSZ   RSS
29827 67111332 25167472

Killed

Статус виходу:

137

що за правилом 128 + сигнал сигналу означає, що ми отримали номер сигналу 9, який man 7 signalговорить про SIGKILL , який надсилається вбивцею поза пам'яті Linux .

Інтерпретація результатів:

  • Віртуальна пам'ять VSZ залишається постійною на рівні printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( psзначення є в KiB) після mmap.
  • RSS "реальне використання пам'яті" ліниво збільшується лише тоді, коли ми торкаємось сторінок. Наприклад:
    • Перший друк у нас є extra_memory_committed 0, а це означає, що ми ще не торкнулися жодної сторінки. RSS - це невеликий 1648 KiBрозмір, який виділяється для нормального запуску програми, наприклад, текстова область, глобальна сфера тощо.
    • у другому друку ми написали 8388608 KiB == 8GiBвартістю сторінок. Як результат, RSS збільшився рівно на 8GIB до8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS продовжує збільшуватися з кроком 8 Гбіт. Останній друк показує близько 24 ГБ пам’яті, і до того, як можна було надрукувати 32 Гб, вбивця ООМ вбив процес

Дивіться також: Потрібно пояснення щодо розміру постійного набору / віртуального розміру

Журнали вбивць OOM

Наші dmesgкоманди показали журнали вбивць OOM.

Точне тлумачення цих запитань було запропоновано на:

Першим рядком журналу був:

[ 7283.479087] mongod invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

Тож ми бачимо, що цікаво, що саме демон демону MongoDB завжди працює в моєму ноутбуці на тлі, який вперше спровокував вбивцю OOM, імовірно, коли бідна людина намагалася виділити трохи пам'яті.

Однак вбивця ООМ не обов'язково вбиває того, хто його пробудив.

Після виклику ядро ​​друкує таблицю або процеси, включаючи oom_score:

[ 7283.479292] [  pid  ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
[ 7283.479303] [    496]     0   496    16126        6   172032      484             0 systemd-journal
[ 7283.479306] [    505]     0   505     1309        0    45056       52             0 blkmapd
[ 7283.479309] [    513]     0   513    19757        0    57344       55             0 lvmetad
[ 7283.479312] [    516]     0   516     4681        1    61440      444         -1000 systemd-udevd

і далі ми бачимо, що наш власний маленький main.outнасправді був убитий під час попереднього виклику:

[ 7283.479871] Out of memory: Kill process 15665 (main.out) score 865 or sacrifice child
[ 7283.479879] Killed process 15665 (main.out) total-vm:67111332kB, anon-rss:92kB, file-rss:4kB, shmem-rss:30080832kB
[ 7283.479951] oom_reaper: reaped process 15665 (main.out), now anon-rss:0kB, file-rss:0kB, shmem-rss:30080832kB

Цей журнал згадує, score 865який цей процес мав, мабуть, найвищий (найгірший) бал вбивць ОМВ, як згадується в: Як вбивця МОМ вирішує, який процес вбити першим?

Цікаво також, що все, мабуть, сталося так швидко, що перед тим, як звільнити пам'ять, процес oomзнову пробудився DeadlineMonitor:

[ 7283.481043] DeadlineMonitor invoked oom-killer: gfp_mask=0x6200ca(GFP_HIGHUSER_MOVABLE), order=0, oom_score_adj=0

і цього разу вбив якийсь процес Chromium, який, як правило, у моїх комп’ютерах - звичайна свиня пам'яті:

[ 7283.481773] Out of memory: Kill process 11786 (chromium-browse) score 306 or sacrifice child
[ 7283.481833] Killed process 11786 (chromium-browse) total-vm:1813576kB, anon-rss:208804kB, file-rss:0kB, shmem-rss:8380kB
[ 7283.497847] oom_reaper: reaped process 11786 (chromium-browse), now anon-rss:0kB, file-rss:0kB, shmem-rss:8044kB

Тестовано в Ubuntu 19.04, Linux kernel 5.0.0.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.