GNU/Linux >> Belajar Linux >  >> Linux

Perlu penjelasan mengenai Resident Set Size/Virtual Size

RSS adalah berapa banyak memori yang dimiliki proses ini di memori utama (RAM). VSZ adalah berapa banyak total memori virtual yang dimiliki proses. Ini mencakup semua jenis memori, baik dalam RAM maupun yang ditukar. Angka-angka ini bisa miring karena mereka juga menyertakan pustaka bersama dan jenis memori lainnya. Anda dapat memiliki lima ratus contoh bash berjalan, dan ukuran total jejak memori mereka tidak akan menjadi jumlah nilai RSS atau VSZ mereka.

Jika Anda perlu mendapatkan gambaran yang lebih mendetail tentang jejak memori suatu proses, Anda memiliki beberapa opsi. Anda dapat melalui /proc/$PID/map dan singkirkan hal-hal yang tidak Anda sukai. Jika itu adalah pustaka bersama, perhitungannya bisa menjadi rumit tergantung pada kebutuhan Anda (yang menurut saya saya ingat).

Jika Anda hanya peduli dengan ukuran tumpukan proses, Anda selalu dapat mengurai [heap] entri di map mengajukan. Ukuran yang dialokasikan kernel untuk tumpukan proses mungkin atau mungkin tidak mencerminkan jumlah pasti byte yang telah diminta untuk dialokasikan. Ada detail kecil, internal kernel, dan pengoptimalan yang dapat membuang ini. Di dunia yang ideal, itu akan sebanyak yang dibutuhkan proses Anda, dibulatkan ke kelipatan terdekat dari ukuran halaman sistem (getconf PAGESIZE akan memberi tahu Anda apa itu — di PC, mungkin 4.096 byte).

Jika Anda ingin melihat berapa banyak memori yang telah dialokasikan oleh suatu proses , salah satu cara terbaik adalah mengabaikan metrik sisi kernel. Sebagai gantinya, Anda memperlengkapi fungsi heap (de)alokasi memori pustaka C dengan LD_PRELOAD mekanisme. Secara pribadi, saya sedikit menyalahgunakan valgrind untuk mendapatkan informasi tentang hal semacam ini. (Perhatikan bahwa penerapan instrumentasi akan membutuhkan proses ulang.)

Harap perhatikan, karena Anda mungkin juga melakukan pembandingan runtime, yaitu valgrind akan membuat program Anda sedikit lebih lambat (tetapi mungkin dalam toleransi Anda).


Contoh minimal yang dapat dijalankan

Agar masuk akal, Anda harus memahami dasar-dasar paging:https://stackoverflow.com/questions/18431261/how-does-x86-paging-work dan khususnya bahwa OS dapat mengalokasikan memori virtual melalui tabel halaman / pembukuan memori internalnya (memori virtual VSZ) sebelum benar-benar memiliki penyimpanan cadangan pada RAM atau disk (memori residen RSS).

Sekarang untuk mengamati tindakan ini, mari buat program yang:

  • mengalokasikan lebih banyak RAM daripada memori fisik kita dengan mmap
  • menulis satu byte pada setiap halaman untuk memastikan bahwa setiap halaman tersebut beralih dari memori hanya virtual (VSZ) ke memori yang benar-benar digunakan (RSS)
  • memeriksa penggunaan memori proses dengan salah satu metode yang disebutkan di: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 upstream.

Kompilasi dan jalankan:

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

di mana:

  • 0x1000000000 ==64GiB:2x RAM fisik komputer saya sebesar 32GiB
  • 0x200000000 ==8GiB:cetak memori setiap 8GiB, jadi kita harus mendapatkan 4 cetakan sebelum crash sekitar 32GiB
  • echo 1 | sudo tee /proc/sys/vm/overcommit_memory :diperlukan untuk Linux agar kami dapat membuat panggilan mmap lebih besar dari RAM fisik:https://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate/57687432#57687432

Keluaran program:

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

Status keluar:

137

yang dengan aturan angka sinyal 128 + berarti kita mendapat nomor sinyal 9 , yang man 7 signal adalah SIGKILL, yang dikirim oleh Linux out-of-memory killer.

Interpretasi keluaran:

  • Memori virtual VSZ tetap konstan pada printf '0x%X\n' 0x40009A4 KiB ~= 64GiB (ps nilainya dalam KiB) setelah mmap.
  • RSS "penggunaan memori nyata" meningkat lambat hanya saat kita menyentuh halaman. Misalnya:
    • pada cetakan pertama, kami memiliki extra_memory_committed 0 , yang berarti kami belum menyentuh halaman apa pun. RSS adalah 1648 KiB kecil yang telah dialokasikan untuk memulai program normal seperti area teks, global, dll.
    • pada cetakan kedua, kami telah menulis ke 8388608 KiB == 8GiB senilai halaman. Akibatnya, RSS meningkat tepat 8GIB menjadi 8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS terus meningkat dalam peningkatan 8GiB. Cetakan terakhir menunjukkan sekitar 24 GiB memori, dan sebelum 32 GiB dapat dicetak, pembunuh OOM mematikan prosesnya

Lihat juga:Perlu penjelasan tentang Resident Set Size/Virtual Size

Log pembunuh OOM

dmesg kami perintah telah menunjukkan log pembunuh OOM.

Interpretasi yang tepat dari itu telah ditanyakan di:

  • https://stackoverflow.com/questions/9199731/understanding-the-linux-oom-killers-logs tetapi mari kita lihat sekilas di sini.
  • https://serverfault.com/questions/548736/how-to-read-oom-killer-syslog-messages

Baris pertama log adalah:

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

Jadi kami melihat bahwa yang menarik adalah daemon MongoDB yang selalu berjalan di laptop saya di latar belakang yang pertama kali memicu pembunuh OOM, mungkin saat orang malang itu mencoba mengalokasikan sebagian memori.

Namun, pembunuh OOM tidak serta merta membunuh orang yang membangunkannya.

Setelah pemanggilan, kernel mencetak sebuah tabel atau proses termasuk 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

dan lebih jauh ke depan kita melihat main.out kecil kita sendiri sebenarnya terbunuh pada doa sebelumnya:

[ 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

Log ini menyebutkan score 865 proses mana yang memiliki, mungkin skor pembunuh OOM tertinggi (terburuk) seperti yang disebutkan di:Bagaimana pembunuh OOM memutuskan proses mana yang akan dibunuh lebih dulu?

Yang juga menarik, semuanya tampaknya terjadi begitu cepat sehingga sebelum memori yang dibebaskan diperhitungkan, oom dibangunkan lagi oleh DeadlineMonitor proses:

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

dan kali ini yang mematikan beberapa proses Chromium, yang biasanya merupakan memory hog normal komputer saya:

[ 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

Diuji di Ubuntu 19.04, kernel Linux 5.0.0.


Linux
  1. Perintah Linux:menjelajahi memori virtual dengan vmstat

  2. Tingkatkan ukuran disk virtual Windows10 VM di QEMU-KVM

  3. Linux – Perlu Penjelasan Tentang Ukuran Resident Set/Ukuran virtual?

  1. Mulai Zathura Layar Penuh / Ingat Ukuran Jendela?

  2. Penggunaan memori puncak dari proses linux/unix

  3. Penggunaan memori dari proses saat ini di C

  1. Cara Memeriksa Ukuran Ram di Baris Perintah Linux di Gb

  2. malloc mengembalikan memori atau ruang alamat virtual

  3. Ketika proses bercabang, apakah memori virtual atau residennya disalin?