drkdev

top을 통해 확인할 수 있는 프로세스 정보들

Dec 28, 2023  │  #linux   #system_engineering   #korean   #kernel  

top

top -hv|-bcEeHiOSs1 -d secs -n max -u|U user -p pids -o field -w [cols]

test

커맨드 사용 방법은 리눅스 top 정리 및 설명 을 참고하는 것이 좋습니다.
또한, 표현된 각 column 외에도 다른 metric은 man 페이지를 통해 확인하실 수 있습니다.

여기서는 top을 통해 살펴볼 수 있는 세부적인 여러 process 관련 정보 중 VIRT, RES, SHR, S, PR, NI들을 살펴보도록 하겠습니다.

task

리눅스에서 프로세스와 스레드는 개념적으로 구분되지만, 내부적으로는 모두 ‘태스크’로 관리됩니다.
리눅스 커널은 프로세스와 스레드를 동일한 기본 구조인 ’task_struct’를 사용하여 표현합니다.

sched.h

top의 개선판들

cpu

cpu 사용량

memory

virtual memory (메모리 가상화)

A program selects a memory location using a virtual address. The processor translates this into a physical address and finally the memory controller selects the RAM chip corresponding to that address.

즉, 프로세스는 virtual address를 통해 메모리를 다루지만, 실제 physical address로의 변환은 processor가 수행하는 셈입니다.1

정리하자면,

이며, 특정 가상 주소가 어느 물리 주소에 매핑되어있는지 변환하는 과정을 수행하는 과정은 cpu(processor) 내의 MMU(Memory Management Unit)가 수행합니다. MMU의 역할을 다시 정리하자면, CPU에에서 컴파일된 코드 실행시 가상 주소 메모리 접근이 필요할 때 해당 주소를 물리 주소 값으로 변환해주는 하드웨어 장치입니다.

가상 메모리 서브시스템의 실제 구현 세부 사항을 직접 확인해보고 싶다면 lwn의 관련 아티클2 을 참고할 수 있습니다.

Linux Memory Types

man 페이지에 따르면 프로세스가 소유한 메모리는 다음 4가지로 분류될 수 있습니다. 지금 당장 top의 출력물을 이해하는데 필요한 내용은 아니지만, 이후에 프로세스의 메모리 사용량을 살펴볼 때 필요한 내용이므로 간단히 정리해보도록 하겠습니다.

                             Private | Shared
                         1           |          2
    Anonymous  . stack               |
               . malloc()            |
               . brk()/sbrk()        | . POSIX shm*
               . mmap(PRIVATE, ANON) | . mmap(SHARED, ANON)
              -----------------------+----------------------
               . mmap(PRIVATE, fd)   | . mmap(SHARED, fd)
  File-backed  . pgms/shared libs    |
                         3           |          4

위 사분면에 근거하여, top의 각 세부 정보들을 대략적으로 설명하면 다음과 같습니다.

우리가 흔히 메모리 영역을 heap, stack, data, code로 분류하곤 합니다. 이에 대해 관련 있는 내용은 다음과 같습니다.

top의 출력물에서 관련있는 정보는 다음과 같습니다.

memory commit 과정과 VIRT, RES, SHR

이제 종합적으로 프로세스가 처음 생성될 때 메모리를 할당 받는 과정인 memory commit을 살펴보겠습니다.

가상 메모리 할당 (VIRT): 프로세스가 생성될 때, 커널은 해당 프로세스에 대해 가상 메모리 공간(VIRT)을 할당합니다. 이 공간은 프로세스가 사용할 수 있는 메모리의 최대 범위를 나타내지만, 처음에는 실제 물리 메모리에 할당되지 않습니다. 이것은 메모리를 효율적으로 관리하고, 실제로 필요할 때만 물리 메모리를 사용하도록 하는 것입니다.

실제 메모리 할당과 페이지 폴트 (RES): 프로세스가 운영되는 동안, 특히 쓰기 작업이 시작될 때, 가상 메모리의 일부가 실제로 필요하게 됩니다. 이때 ‘페이지 폴트(page fault)‘가 발생합니다. 페이지 폴트는 매핑된 물리적 메모리가 페이지 테이블에 존재하지 않을 때 발생하는 것으로, 이를 통해 커널은 해당 가상 메모리 공간에 대응하는 물리 메모리(RES)를 할당하게 됩니다.

공유 메모리 (SHR): 공유 라이브러리와 같이 여러 프로세스에서 공유되는 메모리 영역은 SHR(공유 메모리)로 표시됩니다. 이러한 메모리는 다른 프로세스와 공유되는 물리적 메모리 주소를 가리키며, 각 프로세스의 가상 메모리 공간을 통해 동일한 물리적 메모리 주소를 참조하게 됩니다. 이는 메모리 사용의 효율성을 높이고, 중복을 방지하는 역할을 합니다.

VIRT, RES, SHR에 대한 man 페이지 설명
1.  VIRT  --  Virtual Memory Size (KiB)
The total amount of virtual memory used by the task.  It includes all code, data and shared libraries plus  pages  that  have
been swapped out and pages that have been mapped but not used.

22. RES  --  Resident Memory Size (KiB)
A subset of the virtual address space (VIRT) representing the non-swapped physical memory a task is currently using.   It  is
also the sum of the RSan, RSfd and RSsh fields.
It  can  include  private anonymous pages, private pages mapped to files (including program images and shared libraries) plus
shared anonymous pages.  All such memory is backed by the swap file represented separately under SWAP.
Lastly, this field may also include shared file-backed pages which, when modified, act as a dedicated swap file and thus will
never impact SWAP.

30. SHR  --  Shared Memory Size (KiB)
A subset of resident memory (RES) that may be used by other processes.  It will include shared  anonymous  pages  and  shared
file-backed pages.  It also includes private pages mapped to files representing program images and shared libraries.

memory overcommit

프로세스가 요구하는 메모리의 양은 결국 물리 메모리 공간의 한계를 넘을 수 없다.
넘게 된다면 커널 파라미터인 vm.overcommit_memory의 정의에 따라 커널이 처리한다.3

sysctl vm.overcommit_memory # can be 0, 1, 2

process status

맨 페이지를 통해 좀 더 자세한 내용을 살펴보자.

29. S  --  Process Status
    The status of the task which can be one of:
        D = uninterruptible sleep
        I = idle
        R = running
        S = sleeping
        T = stopped by job control signal
        t = stopped by debugger during trace
        Z = zombie

아래는 프로세스가 생성되고 변하는 상태의 변화를 나타낸 그림입니다.

출처 : https://www.informit.com/articles/article.aspx?p=370047

좀비 프로세스 제거를 위한 wait syscall

보통 좀비 프로세스를 제거하기 위해서 child process의 종료 시그널을 받아 처리하는 방식을 사용하곤 합니다. 아래는 간략한 예시입니다.

void handle_child(int sig) {
    if (sig == SIGCHLD) {
        int status;

        // 성공시 종료된 ps의 pid반환.
        // 특정 pid가 아닌 -1을 전달함으로써 임의의 자식 프로세스 종료를 처리함.
        pid_t id = waitpid(-1, &status, WNOHANG); // non blocking
        if (WIFEXITED(status)) {
            printf("remove ps id : %d\n", id);
            printf("child send : %d\n", WEXITSTATUS(status));
        }
    }
}

int main(int argc, char* argv[]) {
    pid_t pid = 0;

    // signal handling
    struct sigaction act;
    act.sa_handler = handle_child;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;
    sigaction(SIGCHLD, &act, 0);

    ... 생략

    return 0;
}

PR, NI

run queue에 잡힌 task들은 PR(priority)와 NI(nice)에 따라 스케쥴러의 알고리즘에 의해 cpu를 할당받게 됩니다.

실제로 PR이 낮으면 먼저 dispatcher가 cpu에게 처리해줄 것을 요청한다. NI는 PR의 조정값으로, 우선순위는 PR + NI로 결정된다.

예를 들어 process A의 PR: 10, NI: 0, process B의 PR: 10, NI: 5라면, process A가 10, process B가 15의 우선순위를 갖게 된다. 따라서 process A가 먼저 cpu를 할당받게 된다.

nice는 renice로 조정될 수 있습니다.

# Niceness values range from -20 (most favorable to the process) to 19 (least favorable to the process).

# Change priority of a running process:
renice -n niceness_value -p pid

# Change priority of all processes owned by a user:
renice -n niceness_value -u user

# Change priority of all processes that belong to a process group:
renice -n niceness_value --pgrp process_group

다만, 수작업으로 우선순위를 조정하는 경우는 흔치 않으며, 조정으로 인한 차이가 극적이지 않아 대부분 기본값으로 사용한다.


  1. 이러한 가상(logical, virtual)과 물리(physical)의 분리는 메모리 뿐만 아니라 스토리지, 네트워크 등의 분야에서 자주 접하게 되는 패턴입니다. 물리를 감싸는 하나의 추상화된 레이어를 제공하여 파편화 해결, 공유화 등의 이점을 제공할 수 있습니다. ↩︎

  2. https://lwn.net/Articles/253361/  ↩︎

  3. https://mjmwired.net/kernel/Documentation/vm/overcommit-accounting  ↩︎