Skip to main content

Command Palette

Search for a command to run...

Linux top Command에 대한 이해

top 명령어로 더욱 안정적인 시스템 구성하기

Updated
•8 min read•View as Markdown
Linux top Command에 대한 이해
주

I'm Engineer interested in DevOps 🔗 , SRE Engineering 🧰 , Data Engineering 📈 , MLOps 🤖!

Linux top Command에 대한 이해 - top 명령어로 더욱 안정적인 시스템 구성하기

  • 해당 문서는 책 'DevOps와 SE를 위한 리눅스 커널 이야기' 를 읽고 정리한 내용 입니다.

OverView

저는 container 환경을 좋아합니다. DockerFile만 코드에 갖고있다면 어디서든 빠르게 배포하고 어떠한 구조로 Application이 동작하는지 간편하게 확인할 수 있기 때문입니다.

심지어는 Private Image Repository 건 , Public Image Repository 건 상관 없이 Docker Image만 Repository에 push 되어 있다면, 간단한 docker 명령어로 배포할수 있다는점도 너무 좋습니다.

허나 이렇게 간편하고 좋은 Docker container 또한 알고보면 linux 프로세스입니다.

linux cgroup과 namespace를 기반으로 독립적이게 구성되어진 것이기에, container 또한 호스트 linux에 실행중인 프로세스에 불과합니다.

따라서 container 조차 linux에 완전히 격리된 상태가 아니기에,, 서버를 관리하는 입장에선 프로세스의 상태를 확인하고 상황에 따라 조치를 취하는것이 정말 중요합니다.

리눅스 프로세스를 감시하는 방법은 다양하지만, 시스템 상태를 빠르고 간편하게 파악할 수 있는 linux top command를 정리해 보았습니다.

top 구경하기

일단 top 명령어를 수행해서 확인해 봅니다.

$ top 

# 하단은 수행결과
------
top - 14:57:45 up 20 min,  1 user,  load average: 0.00, 0.01, 0.04
Tasks:  98 total,   1 running,  97 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
MiB Mem :    949.6 total,    339.6 free,    133.5 used,    476.4 buff/cache
MiB Swap:      0.0 total,      0.0 free,      0.0 used.    668.8 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
      1 root      20   0  105672  16860  10112 S   0.0   1.7   0:01.21 systemd
      2 root      20   0       0      0      0 S   0.0   0.0   0:00.00 kthreadd
      3 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 rcu_gp
      4 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 rcu_par_gp
      5 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 slub_flushwq
      6 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 netns
      8 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 kworker/0:0H-events_highpri
     10 root       0 -20       0      0      0 I   0.0   0.0   0:00.00 mm_percpu_wq
     11 root      20   0       0      0      0 I   0.0   0.0   0:00.00 rcu_tasks_kthread
...

많은 정보들을 확인할 수 있습니다 ! 명령어 결과 하나씩 뜯어보겠습니다.

  • 14:53:22 up 16 min

    • 순차적으로 시스템 시간과, 해당 서버가 구동된지 얼만큼 지낫는지 나타냅니다. : 켜진지 16분 되었습니다.
  • 1 user, load average: 0.00, 0.01, 0.04

    • 순차적으로 몇명의 유저가 로그인했는지 , load average가 어느정도인지 확인할 수 있습니다.

      load average가 높을수록 서버 부하가 높다는 의미입니다.

  • Tasks: 98 total, 1 running, 97 sleeping, 0 stopped, 0 zombie

    • 서버에 몇개의 프로세스가 구동되고 있는지 확인할 수 있습니다.

      Total 98개가 구동중이며, 1개가 실행중이고 97개가 sleep, stop과 zombie 프로세스는 0개로 확인됩니다.

  • %Cpu(s): 0.0 us ... 부터 3번째줄 668.8 avail Mem 까지

    • 각각 CPU 사용량 , Memory 사용량 , swap 메모리 사용량을 나타냅니다.

      출력 위치로 swap 메모리 사용 여부가 시스템 상태에 중요한 영향을 미친다는것을 알 수 있습니다.

또한 하단 위 순차적으로 PR , NI , VIRT , RES , SHR , S 는 중요합니다.

  • PR
    • 프로세스 실행 우선순위
  • NI

    • PR 을 얼마나 조절할 것인지 결정 ,

      기본 PR 값에 NI 를 더해 실제 PR 값을 알 수 있음.

  • VIRT , RES , SHR
    • 프로세스가 사용하고있는 메모리의 양
  • S
    • 프로세스의 현재 상태를 나타냄

OverView 한 내용입니다.

각기 가지고있는 내용이 다르고 상세하게 다시 작성합니다.

VIRT , RES , SHR

이들은 프로세스가 사용하고 있는 메모리의 양을 나타냅니다.

각자 의미하는 바가 다른데,

  • VIRT : task가 사용하고 있는 virtual memory의 전체 용량
  • RES : task가 사용하고 있는 물리 메모리의 양
  • SHR : 다른 프로세스와 공유하고 있는 shared memory 의 양

을 의미합니다.

VIRT 값은 가상메모리의 전체 용량을 의미합니다. 따라서 값이 높더라도 문제가 되지 않습니다.

그리고 SHR 값은 shared memory의 양을 나타냅니다.

공유 메모리의 예로 라이브러리가 있습니다.

리눅스 프로세스가 대부분 사용하는 라이브러리중 glibc 라는 친구가 있습니다. 이를 모든 프로세스가 메모리에 올려서 사용하는것은 메모리 낭비이기에.

리눅스는 shared memory 공간을 따로 만들어서 공유되는 라이브러리들을 이 공간에 넣어두고 프로세스들이 참조해서 사용하게끔 합니다.

  • like spring bean factory ?

그리고 SHR은 실제 사용중인 물리 메모리의 양을 나타냅니다. 따라서 메모리 점유율이 높은 프로세스를 찾는다면, SHR 값이 높은 프로세스를 찾는것이 적합합니다.

1. VIRT 메모리 공간

VIRT 로 표현되는 메모리는,. 프로세스가 커널로부터 사용을 예약받은 메모리 공간입니다.

프로세스는 커널에게 시스템 콜을 이용해서, 커널에게 자신이 필요한 메모리공간을 할당해 줄 것을 요청하게 되는데, 커널은 가용 메모리공간이 있다면 메모리공간을 할당해 줍니다.

중요한것은 할당해준 순간에도 커널은 실제 물리적 메모리를 할당해준것은 아닙니다.

이러한 동작 방식을 Memory Commit 이라 하며, vm.overcommit_memory 를 통해 동작 방식을 정의하는것 이 가능합니다. 아래는 설정 가능한 파라미터 값 입니다.

  • 0 : default. 요청 메모리가 물리적 RAM 보다 많은지 확인. 실제로 사용 가능한 RAM 보다 많은 메모리를 할당하려 할 때 OOM(Out Of Memory 발생)
  • 1 : 항상 메모리가 overcommit 되도록 함. 요청 메모리 양에 관계없이 모든 요청을 허용하고 , 메모리 부족 시 OOM Killer를 사용하여 프로세스를 강제로 종료시킴.
  • 2 : 메모리가 항상 overcommit 되지 않도록 함. 메모리 요청이 시스템의 실제 메모리 양을 초과하면 메모리 할당이 거부됨. 안정적인 시스템 운영이 가능한 옵션..

프로세스가 할당된 메모리 영역에 쓰기가 발생했을 때 Page fault가 발생하며 물리 메모리 영역이 할당되고, 물리 메모리 영역에 할당된 영역이 RES 로 계산됩니다.

여러 프로세스가 동일한 코드나 데이터를 실행할 때, 커널은 이러한 물리적 메모리 페이지를 공유합니다.

만약 같은 코드를 가진 자바 프로그램 두개가 동시에 작동할 경우에, 커널은 두개 프로세스의 메모리 페이지를 모두 RES 로 공유합니다.

이러한 메커니즘은 물리적 메모리를 절약할 수 있다는 이점이 있습니다.

2. Memory Commit 방식

프로세스는 항상 메모리 공간이 필요합니다. 그리고 커널은 실제 물리적 메모리공간을 가지고 있습니다.

프로세스가 커널에게 필요한 공간을 요청하면 , 커널은 프로세스에게 사용 가능한 메모리 영역을 부여하고 실제로 주진 않습니다.

그러나 주었다는것을 기록만 해둡니다.

이를 Memory Commit 방식이라 합니다.

2.1 왜 사용할까 ?

fork() 시스템 콜을 예시로 들어봅니다.

fork() 시스템 콜은 , 새로운 프로세스를 만들어냅니다. 이는 기존 프로세스와 동일한 메모리 영역을 가지는것이 아니라 새로운 프로세스에게 부모 프로세스와 동일한 메모리 공간을 복재하는데 , 이때 메모리 할당 및 초기화가 필요합니다.

이런 상황에서 Memory Commit이 필요합니다.

새로운 프로세스에게 무조건 물리적 공간을 할당해주지 않고, 예약해 두고 있다가 필요할때만 할당해주면서 메모리를 효율적으로 사용함에 Memory Commit 방식을 사용합니다.

3. Memory Commit을 통한 시스템 점검

그렇다면 Memory Commit비율이 높을 경우 , 순간 많은양의 메모리를 커널이 프로세스에게 할당해줄 수 있습니다.

이러한 상황은 시스템에 큰 부하를 일으키기 때문에 , 상황에 따라 vm.overcommit_memory 파라미터를 조절하며 사용해야 합니다.

해당 파라미터를 적절히 조절하여 , VIRT 메모리 양을 조절하고 commit 비율을 적정 수준으로 맞추며 시스템 안정성을 높혀야만 합니다.

Memory Commit 비율은 sar 명령어로 확인이 가능합니다.

  • %commit 섹션이 메모리 커밋 비율입니다.. ```bash sar -r Linux 6.1.79-99.167.amzn2023.x86_64 (localhost) 04/02/24 _x8664 (1 CPU)

14:37:11 LINUX RESTART (1 CPU)

14:40:34 kbmemfree kbavail kbmemused %memused kbbuffers kbcached kbcommit %commit kbactive kbinact kbdirty 14:50:34 305704 638728 145804 15.00 2168 440888 252308 25.95 181192 314500 0 15:00:34 347784 684836 99720 10.26 2168 444900 247860 25.49 202720 292796 12 15:10:34 347656 684860 99692 10.25 2168 445052 249112 25.62 202740 293632 12 ...


## 프로세스 상태 확인 - ```S

다시 top 명령어로 돌아와서 , S 섹션인 각 프로세스 상태값에 대해 확인해 봅니다.

SHR 우측에 위치해 있는데, 각 값은 다음과 같은 의미를 갖습니다.

  • D

    Uninterruptible sleep 상태 , 디스크 혹은 네트워크 I/O를 대기하고있는 프로세스. I/O 가 완료되기 전까지 필요한 리소스를 사용할 수 없음.

  • R

    실행중인 프로세스 실제로 CPU 자원을 소비중인 프로세스

  • S

    sleeping 상태의 프로세스 , D 상태와 다른점은 , S 상태 프로세스는 요청한 리소스를 즉시 사용할 수 있다는것에 있음. 실행을 위해 특정 이벤트 (I/O , Event 수신 등) 가 필요함.

  • T

    traced or stopped 프로세스 , strace 등으로 프로세스 시스템 콜을 추적하고 있는 상태. 자주볼순 없음

  • Z

    Zombie 상태 프로세스 , 부모 프로세스가 죽은 자식 프로세스.

    커널의 PID 값은 한계치를 갖고 있는데, 이 상태의 프로세스가 많아질수록 PID를 많이 점유함. 결국 PID 고갈 문제가 발생할 수 있음.

    sysctl -a | grep -i pid_max 명령어로 최대 PID 개수 확인 가능

  • I

    Idle(유휴) 상태 프로세스, 대기 상태로 있지만 , 인터럽트 처리를 위해 대기중인 경우 사용. 대체로 커널스레드에서 볼 수 있음.

프로세스 상태 life cycle

프로세스 상태는 실행중일 때 계속해서 변화하게 됩니다.

따라서 상태를 감시하는것 또한 시스템 안정성을 지키는데 주요하게 작동합니다.

process_life_cycle

S 상태가 많다고 해서 문제가되진 않지만, (콘솔입력이나 sleep() 등으로 멈춘 프로세스..) D 상태 프로세스가 많은것은 문제가 될 수 있습니다.

D 상태인 프로세스는 I/O 이벤트 등을 대기하고 있는것이기에 특정 요청응답을 대기하고 있다는 것이고, 이들은 끝나면 다시 R 상태가 되어야만 하기 때문에 문제가 될 수 있습니다.

  • 이는 실제 물리 메모리자원을 할당받아야 함

라이프사이클 상태를 잘 감시해서, Z 프로세스를 정리하거나 D 상태 프로세스들이 많다면 해결방안을 강구하는것이 시스템을 안정적으로 운영하는데 필요한 작업일 것 입니다.

프로세스 우선순위 - PR , NI

VIRT 좌측 세번째 , 네번째 컬럼인 PR 과 NI 는 프로세스를 스케줄링 할 때 우선순위를 나타냅니다.

프로세스가 스케줄링 되기 위해선 , 다음 라이프사이클을 따릅니다.

생성(Create) -> 반복 {

준비(Ready) -> 실행(Running) -> 차단(Blocked) -> 컨텍스트 스위칭 -> 실행(Running) } -> 종료(Terminate)

컨텍스트 스위칭은 프로세스가 메모리를 점유하고있을 때 , 점유시간이 길어지면 프로세스는 준비상태 큐로 다시 돌아가고 큐에 있던 다음 프로세스가 메모리를 점유하는 방식입니다.

다시 top으로 돌아와서 각 컬럼은 다음을 의미합니다.

  • PR

    우선순위 값

  • NI

    nice 값, 명령어로 우선순위를 낮출 때 사용

또한 실제 우선순위 값은 , PR + NI 공식을 통해 구할 수 있습니다.

PR : 0 NI : -20

일 경우

실제 PR : -20

우선순위는 값이 낮을수록 우선순위가 높은 것으로 , nice 명령을 통해 PR , 실제 우선순위 값을 낮출 수 있고 이는 프로세스가 준비상태 큐에서 나오는 순서가 더 앞쪽으로 옮겨진다는것을 의미하기에 , 더 많이 스케줄링될 수 있음을 나타냅니다.

만약 동일한 우선순위를 가진 프로세스들이 동작중일때, 더 낮은 PR을 가진 프로세스가 나타나면 , 원래 실행중인 프로세스는 다른 CPU를 점유하거나 대기상태로 돌아가고, 무조건 낮은 프로세스가 먼저 수행됩니다.

이 둘 이외에 RT 로 표현되는 프로세스도 존재합니다.

RT

RealTime 스케줄러 , 일반적 사용자가 생성한 프로세스가 아닌 특정 시간안에 종료되어야 하는 프로세스.

커널에서 사용하는 데몬들이 대상임.

결론

top 명령어 하나만 가지고도 리눅스 서버의 전반적인 상태를 파악하는것이 가능합니다.

프로세스 라이프사이클을 생각하여 각 프로세스의 우선순위를 스케줄링하거나 , Memory Commit의 비율을 낮추어 특정 시점에 메모리 부하를 낮추고 RES 공간을 잘 활용하여 서버의 메모리를 최적화 할 수 있습니다.

서버리스와 container 환경이 많아짐에 따라 리눅스 자체적인 관심이 줄어드는것 같은 분위기가 있는것 같아 보입니다.

저 또한 Kubernetes를 먼저 마주했기에 , 리눅스 커널에대한 관심도는 낮았습니다.

그러나 container도 리눅스의 프로세스이고, Kubernetes또한 리눅스 시스템 위에서 동작하는 프로세스이기에 , 시스템 상태를 파악하고 더욱 안정적인 서버를 유지하는것은 필수적이라 생각합니다.