Cần giải thích về Kích thước cài đặt thường trú / Kích thước ảo


61

Tôi thấy rằng đó pidstatsẽ là một công cụ tốt để theo dõi các quá trình. Tôi muốn tính toán mức sử dụng bộ nhớ trung bình của một quá trình cụ thể. Dưới đây là một số ví dụ đầu ra:

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

(Đây là một phần của đầu ra từ pidstat -r -p 7276.)

Tôi có nên sử dụng thông tin Kích thước cài đặt thường trú (RSS) hoặc Kích thước ảo (VSZ) để tính mức tiêu thụ bộ nhớ trung bình không? Tôi đã đọc một vài điều trên Wikipedia và trên các diễn đàn nhưng tôi không chắc chắn để hiểu đầy đủ về sự khác biệt. Thêm vào đó, dường như không ai trong số họ đáng tin cậy. Vì vậy, làm thế nào tôi có thể theo dõi một quá trình để có được việc sử dụng bộ nhớ của nó?

Bất kỳ trợ giúp về vấn đề này sẽ hữu ích.



Câu trả lời:


63

RSS là bao nhiêu bộ nhớ mà quá trình này hiện có trong bộ nhớ chính (RAM). VSZ là tổng số bộ nhớ ảo mà quá trình có tổng cộng. Điều này bao gồm tất cả các loại bộ nhớ, cả trong RAM và trao đổi. Những con số này có thể bị sai lệch bởi vì chúng cũng bao gồm các thư viện dùng chung và các loại bộ nhớ khác. Bạn có thể có năm trăm trường hợp bashchạy và tổng kích thước của dấu chân bộ nhớ của họ sẽ không phải là tổng giá trị RSS hoặc VSZ của họ.

Nếu bạn cần có được một ý tưởng chi tiết hơn về dấu chân bộ nhớ của một quá trình, bạn có một số tùy chọn. Bạn có thể đi qua /proc/$PID/mapvà loại bỏ những thứ bạn không thích. Nếu đó là các thư viện dùng chung, việc tính toán có thể trở nên phức tạp tùy thuộc vào nhu cầu của bạn (mà tôi nghĩ là tôi nhớ).

Nếu bạn chỉ quan tâm đến kích thước heap của quá trình, bạn luôn có thể phân tích cú pháp [heap]mục trong maptệp. Kích thước mà kernel đã phân bổ cho heap process có thể hoặc không thể phản ánh chính xác số byte mà tiến trình đã yêu cầu được phân bổ. Có những chi tiết nhỏ, nội bộ hạt nhân và tối ưu hóa có thể loại bỏ điều này. Trong một thế giới lý tưởng, nó sẽ có nhiều như quy trình của bạn cần, được làm tròn đến bội số gần nhất của kích thước trang hệ thống ( getconf PAGESIZEsẽ cho bạn biết đó là gì - trên PC, có lẽ là 4.096 byte).

Nếu bạn muốn xem một quá trình được phân bổ bao nhiêu bộ nhớ , một trong những cách tốt nhất là từ bỏ các số liệu bên hạt nhân. Thay vào đó, bạn sử dụng các hàm cấp phát bộ nhớ heap của thư viện C với LD_PRELOADcơ chế. Cá nhân, tôi hơi lạm dụng valgrindđể có được thông tin về loại điều này. (Lưu ý rằng việc áp dụng thiết bị sẽ yêu cầu khởi động lại quy trình.)

Xin lưu ý, vì bạn cũng có thể là thời gian chạy chuẩn, điều đó valgrindsẽ làm cho chương trình của bạn chậm hơn một chút (nhưng có thể trong phạm vi dung sai của bạn).


Cảm ơn rất nhiều! Tôi sẽ điều tra các tùy chọn khác nhau. Bạn đã được nhiều hơn hữu ích! :)
Flanfl

"Bạn có thể có năm trăm bash đang chạy và tổng kích thước của dấu chân bộ nhớ của chúng sẽ không phải là tổng giá trị RSS hoặc VSZ của chúng." Nhưng liệu tổng giá trị RSS của họ có phải là một xấp xỉ tốt không? Giống như tổng của cột cư trú từ statm, tôi không cần một giá trị chính xác siêu đáng tin cậy, nhưng tôi cần biết mức độ cao mà các quá trình java của tôi đang sử dụng
iloveretards

3
Trên Ubuntu, đó /proc/$PID/mapslà lỗi chính tả hay lỗi phân phối?
dolzenko

1

Ví dụ runnable tối thiểu

Để điều này có ý nghĩa, bạn phải hiểu những điều cơ bản về phân trang: https://stackoverflow.com/questions/18431261/how-does-x86-paging-work và đặc biệt là HĐH có thể phân bổ bộ nhớ ảo thông qua các bảng trang / lưu giữ bộ nhớ trong của nó (bộ nhớ ảo VSZ) trước khi nó thực sự có bộ lưu trữ dự phòng trên RAM hoặc đĩa (bộ nhớ lưu trữ RSS).

Bây giờ để quan sát điều này trong thực tế, hãy tạo một chương trình:

  • phân bổ nhiều RAM hơn bộ nhớ vật lý của chúng tôi với mmap
  • ghi một byte trên mỗi trang để đảm bảo rằng mỗi trang đó đi từ bộ nhớ ảo RSS và VSZ
  • kiểm tra mức sử dụng bộ nhớ của quy trình bằng một trong các phương pháp được đề cập tại: https://stackoverflow.com/questions/1558402/memory-usage-of-civerse- Process-in-c

C chính

#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 ngược dòng .

Biên dịch và chạy:

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

Ở đâu:

Đầu ra chương trình:

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

Trạng thái thoát:

137

mà theo quy tắc số tín hiệu 128 + có nghĩa là chúng ta có số tín hiệu 9, có nghĩa man 7 signalSIGKILL , được gửi bởi kẻ giết người hết bộ nhớ Linux .

Giải thích đầu ra:

  • Bộ nhớ ảo VSZ không đổi tại printf '0x%X\n' 0x40009A4 KiB ~= 64GiB( psgiá trị tính bằng KiB) sau mmap.
  • RSS "sử dụng bộ nhớ thực" chỉ tăng một cách lười biếng khi chúng ta chạm vào các trang. Ví dụ:
    • trên bản in đầu tiên, chúng tôi có extra_memory_committed 0, có nghĩa là chúng tôi chưa chạm vào bất kỳ trang nào. RSS là một phần nhỏ 1648 KiBđã được phân bổ cho khởi động chương trình bình thường như vùng văn bản, toàn cầu, v.v.
    • trên bản in thứ hai, chúng tôi đã viết lên 8388608 KiB == 8GiBgiá trị của các trang. Do đó, RSS tăng chính xác 8GIB lên8390256 KiB == 8388608 KiB + 1648 KiB
    • RSS tiếp tục tăng theo gia số 8GiB. Bản in cuối cùng cho thấy khoảng 24 GiB bộ nhớ và trước khi có thể in 32 GiB, kẻ giết người OOM đã giết quá trình

Xem thêm: Cần giải thích về Kích thước cài đặt thường trú / Kích thước ảo

Nhật ký sát thủ OOM

Các dmesglệnh của chúng tôi đã hiển thị nhật ký sát thủ OOM.

Một cách giải thích chính xác của những người đã được hỏi tại:

Dòng đầu tiên của nhật ký là:

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

Vì vậy, chúng ta thấy điều thú vị đó là daemon MongoDB luôn chạy trong máy tính xách tay của tôi trên nền tảng đầu tiên kích hoạt kẻ giết người OOM, có lẽ là khi người nghèo đang cố phân bổ bộ nhớ.

Tuy nhiên, kẻ giết người OOM không nhất thiết phải giết người đánh thức nó.

Sau khi gọi, kernel in một bảng hoặc các quy trình bao gồm 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

và xa hơn nữa chúng ta thấy rằng chính chúng ta main.outđã bị giết trong lần gọi trước:

[ 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

Nhật ký này đề cập đến score 865quá trình đó, có lẽ là điểm giết người OOM cao nhất (tệ nhất) như đã đề cập tại: Làm thế nào để kẻ giết người OOM quyết định quá trình nào sẽ giết trước?

Cũng thú vị, mọi thứ rõ ràng đã xảy ra nhanh đến mức trước khi bộ nhớ được giải phóng, quá trình oomlại được đánh thức lại DeadlineMonitor:

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

và lần này đã giết một số quy trình Chromium, thường là máy tính của tôi bộ nhớ bình thường:

[ 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

Đã thử nghiệm trong Ubuntu 19.04, nhân Linux 5.0.0.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.