Resident Set Size / Virtual Size hakkında bir açıklama yapmanız gerekiyor.


61

Bunun pidstatsüreçleri izlemek için iyi bir araç olacağını öğrendim . Belirli bir işlemin ortalama bellek kullanımını hesaplamak istiyorum. İşte bazı örnek çıktı:

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

(Bu, çıktının bir parçasıdır pidstat -r -p 7276.)

Ortalama bellek tüketimini hesaplamak için Resident Set Size (RSS) veya Sanal Boyut (VSZ) bilgilerini kullanmalı mıyım? Vikipedi ve forumlarda birkaç şey okudum, ancak farkları tam olarak anladığımdan emin değilim. Ayrıca, hiçbirinin güvenilir olmadığı anlaşılıyor. Peki, bellek kullanımını elde etmek için bir süreci nasıl izleyebilirim?

Bu konuda herhangi bir yardım yararlı olacaktır.



Yanıtlar:


63

RSS, bu işlemin şu anda ana belleğe (RAM) sahip olduğu bellek miktarıdır. VSZ, işlemin toplamda ne kadar sanal belleği olduğunu. Bu, hem RAM hem de değiştirilen tüm bellek türlerini içerir. Paylaşılan kütüphaneleri ve diğer bellek türlerini de içerdiklerinden, bu sayılar çarpık olabilir. Beş yüz bashçalışan örneğiniz olabilir ve bellek alanlarının toplam boyutu RSS veya VSZ değerlerinin toplamı olmayacak.

Bir işlemin bellek ayak izi hakkında daha ayrıntılı bir fikir edinmeniz gerekirse, bazı seçenekleriniz vardır. İstediğin /proc/$PID/mapşeyleri geçip çıkarabilirsin . Paylaşılan kütüphaneler ise, gereksinimlerinize bağlı olarak hesaplama karmaşıklaşabilir (sanırım hatırlıyorum).

Yalnızca işlemin yığın boyutuyla ilgileniyorsanız [heap], mapdosyadaki girişi her zaman yalnızca ayrıştırabilirsiniz . Çekirdek süreç yığın için ayırdığı boyut veya süreç olan bayt sayısını tam yansıtmıyor olabilir sorulan tahsis edilecek. Bunu kaldırabilecek küçük detaylar, çekirdek içseller ve optimizasyonlar var. İdeal bir dünyada, sistem sayfa boyutunun en yakın katına yuvarlanan işlem ihtiyaçlarınız kadar getconf PAGESIZEolacaktır ( size ne olduğunu söyleyecektir - PC'lerde muhtemelen 4.096 bayttır).

Bir işlemin ne kadar hafıza ayırdığını görmek istiyorsanız , en iyi yollardan biri çekirdek taraf metriklerinden kurtulmaktır. Bunun yerine, C kütüphanesinin yığın hafızasını (de) ayırma işlevlerini LD_PRELOADmekanizma ile birlikte kullanırsınız . Şahsen, valgrindbu tür şeyler hakkında bilgi edinmek için biraz suistimal ediyorum . (Enstrümantasyonu uygulamanın işlemin yeniden başlatılmasını gerektireceğini unutmayın.)

Lütfen dikkat, ayrıca çalışma zamanlarını da kıyaslayabildiğiniz valgrindiçin, programlarınızı çok yavaşlatır (ancak muhtemelen toleranslarınız dahilinde).


Çok teşekkürler! Farklı seçenekleri araştıracağım. Daha fazla yardımcı oldun! :)
Flanfl

"Beş yüz bash örneği çalıştırabilir ve bellek alanlarının toplam boyutu RSS veya VSZ değerlerinin toplamı olmaz." Fakat RSS değerlerinin toplamı iyi bir yaklaşım olabilir mi? Statm'deki yerleşik sütunun toplamı gibi, süper güvenilir bir kesin değere ihtiyacım yok, ancak java işlemlerimin ne kadar hafıza kullandığını bilmem gerekiyor
iloveretards 28:17

3
Ubuntu'da bu /proc/$PID/mapsyazım hatası mı yoksa dağıtıcı fark mı?
dolzenko

1

Minimum çalıştırılabilir örnek

Bunun anlaşılması için, çağrı yapmanın temellerini anlamanız gerekir: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work ve özellikle işletim sisteminin sanal tabloları sayfa tabloları aracılığıyla tahsis edebilmesi / RAM veya diskte (RSS yerleşik bellek) bir yedekleme deposuna sahip olmadan önce dahili bellek defteri (VSZ sanal belleği) tutması.

Şimdi bu eylemi gözlemlemek için, hadi bir program yaratalım:

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 yukarı akış .

Derleyin ve çalıştırın:

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

nerede:

Program çıktısı:

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

Çıkış durumu:

137

hangi 128 + sinyal numarası kuralı biz sinyal numarası var demektir 9, man 7 signaldiyor sigkill Linux tarafından gönderilen, dışı belleğe katil .

Çıktı yorumu:

  • VSZ sanal belleği mmap'tan sonra printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( psdeğerler KiB cinsindendir) sabit kalır .
  • RSS "gerçek bellek kullanımı" yalnızca sayfalara dokunduğumuzda tembel bir şekilde artmaktadır. Örneğin:
    • İlk baskıda biz var extra_memory_committed 0, bu da henüz hiçbir sayfaya dokunmadığımız anlamına geliyor. RSS, 1648 KiBmetin alanı, globals, vb. Normal program başlangıcı için tahsis edilmiş küçük bir sayıdır.
    • İkinci baskıda, 8388608 KiB == 8GiBsayfalara değecek kadar yazdık. Sonuç olarak, RSS tam olarak 8GIB arttı8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS 8GiB artışlarla artmaya devam ediyor. Son baskı yaklaşık 24 GiB hafıza gösteriyor ve 32 GiB basılmadan önce OOM katili işlemi öldürdü

Ayrıca bakınız: Resident Set Size / Virtual Size hakkında bir açıklama yapmalısınız

OOM katil günlükleri

Bizim dmesgkomutları OOM katil günlükleri göstermiştir.

Bunların tam olarak yorumlanması istendi:

Kütüğün ilk satırı şuydu:

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

Bu yüzden ilginç bir şekilde, dizüstü bilgisayarımda her zaman arka planda çalışan OOM katilini tetikleyen MongoDB arka plan programı olduğunu görüyoruz, muhtemelen kötü bir şey biraz bellek ayırmaya çalışırken.

Ancak, OOM katilinin onu uyandıran kişiyi öldürmesi şart değildir.

Çağrının ardından, çekirdek aşağıdakileri içeren bir tablo veya işlemleri yazdırır 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

ve daha ileride görüyoruz ki main.out, önceki istilada kendi küçüklerimiz gerçekten öldürüldü:

[ 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

Bu kayıtta, score 865bu işlemin hangisi olduğu, muhtemelen en yüksek (en kötü) OOM katili puanının belirtildiği gibi olduğu belirtilmektedir: OOM katili hangi süreci ilk öldüreceğine nasıl karar verir?

Ayrıca ilginç olarak, her şey görünüşte o kadar hızlı gerçekleşti ki, serbest kalan bellek hesaba katılmadan önce oom, DeadlineMonitorsüreç tekrar uyandı :

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

ve genellikle bilgisayarlarımın normal hafızası olan bazı Chromium işlemlerini öldürdüm:

[ 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 çekirdeği 5.0.0'da test edilmiştir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.