스크랩

htop 화면의 숫자들, 정확히 무슨 뜻일까? Load Average부터 좀비 프로세스까지 완벽 해설

htop 화면의 숫자들, 정확히 무슨 뜻일까? Load Average부터 좀비 프로세스까지 완벽 해설

들어가며

서버에 접속하면 습관처럼 htop이나 top을 켜보지만, 화면에 가득한 숫자들이 정확히 무엇을 의미하는지 자신 있게 설명할 수 있는 분은 의외로 많지 않습니다. 이 글은 Ubuntu Server의 htop 화면을 출발점으로, uptime, load average, 프로세스 상태, 메모리 지표가 실제로 어디서 오고 무엇을 뜻하는지 하나씩 추적한 명글(peteris.rocks, 2019)을 정리한 것입니다.

핵심 요약부터 드리자면:

  • htop이 보여주는 값 대부분은 /proc(procfs)과 /etc/passwd 같은 시스템 파일에서 옵니다
  • Load average는 CPU 사용률이 아닙니다. 실행 중/대기 중 프로세스에 uninterruptible 상태까지 포함한 지수 감쇠 이동평균입니다
  • R, S, D, Z 같은 상태 코드는 프로세스가 왜 멈췄는지, 왜 죽지 않는지 판단하는 결정적 단서입니다
  • VIRT, RES, SHR은 각각 다른 관점의 메모리라서, 숫자 하나만 보고 "이 프로세스가 메모리를 이만큼 쓴다"고 단정하면 안 됩니다

htop의 값은 어디서 올까: /proc과 strace

htop, top, ps 같은 진단 도구는 마법을 부리는 게 아니라 /proc/<pid>/ 아래의 파일을 읽습니다. 어떤 프로그램이 무슨 파일을 읽는지 궁금하다면 strace로 직접 확인할 수 있습니다.

strace -e open uptime

예를 들어 uptime 명령은 /proc/uptime을 읽습니다. 이 파일의 첫 번째 숫자는 시스템이 켜져 있던 총 시간(초), 두 번째는 idle 상태였던 시간(초)입니다. 재미있는 점은 멀티코어 시스템에서는 idle 시간이 코어별로 합산되어 전체 uptime보다 커질 수 있다는 것입니다.

프로세스 정보도 마찬가지입니다:

  • /proc/<pid>/cmdline — 실행 명령어 (인자는 \0 바이트로 구분되어 있어 tr '\0' '\n' < /proc/<pid>/cmdline으로 보면 편합니다)
  • /proc/<pid>/cwd — 현재 작업 디렉터리 링크
  • /proc/<pid>/exe — 실행된 바이너리 링크

Load Average, 가장 많이 오해받는 숫자

/proc/loadavg의 첫 세 값은 최근 1분, 5분, 15분의 load average입니다. 그런데 이 값, CPU 사용률과 같은 것이 아닙니다.

Load는 다음을 세는 값입니다:

  • 실행 중(running)이거나 실행 대기 중(runnable)인 프로세스
  • uninterruptible 상태(D)의 프로세스 — 보통 I/O 대기

게다가 단순 평균이 아니라 지수 감쇠 이동평균입니다. 1분 load average라고 해서 딱 마지막 60초만 반영하는 게 아니라, 최근 1분에 큰 비중을 두면서 이전 활동도 일부 포함합니다.

대략적인 감 잡기

  • 단일 CPU에서 CPU-bound 프로세스 하나 → load 1.00 ≈ CPU 100%
  • 2코어 시스템에서 같은 상황 → CPU 사용률 약 50%
  • 2코어 시스템의 CPU 100%에 해당하는 load ≈ 2.00

다만 이 단순화가 항상 맞지는 않습니다. D 상태 프로세스가 load에 포함되기 때문에, load average는 높은데 CPU 사용률은 낮은 상황(예: NFS 지연, 과도한 swap)이 얼마든지 가능합니다. 순간 CPU 사용률이 궁금하다면 mpstat 1을 쓰세요.

참고로 sleep 30은 sleep 상태라 load를 올리지 않지만, cat /dev/urandom > /dev/null처럼 계속 CPU를 쓰는 작업은 load를 올립니다.

Tasks, PID, 그리고 프로세스 트리

htop 오른쪽 위의 Tasks는 전체 프로세스 수와 실행 중 프로세스 수를 보여줍니다. Linux 커널이 내부적으로 프로세스를 task라고 부르기 때문에 이런 이름이 붙었습니다.

  • Shift+H — thread 표시 토글 (Tasks: 23, 10 thr)
  • Shift+K — kernel thread 표시 토글 (Tasks: 23, 40 kthr)
  • F5 — 프로세스 계층(트리) 보기. ps f, pstree -a와 같은 정보입니다

모든 프로세스에는 PID가 할당되며, PID 1은 부팅 시 시작되는 /sbin/init입니다. bash에서 date를 실행하면 bash가 fork로 자신을 복제하고 exec로 /bin/date를 올린 뒤, parent인 bash가 child의 종료를 wait합니다. 이 parent-child 관계가 프로세스 트리를 이룹니다.

프로세스 사용자와 권한

각 프로세스는 사용자(숫자 UID)에 소유됩니다. /proc/<pid>/status의 Uid 항목에서 확인할 수 있고, id 1000으로 숫자를 이름으로 바꿔볼 수 있습니다. 이름 해석은 /etc/nsswitch.conf 설정에 따라 /etc/passwd, /etc/group(또는 LDAP 등)을 참조합니다.

흥미로운 예외가 setuid입니다. /usr/bin/passwd는 일반 사용자가 실행해도 /etc/shadow에 쓸 수 있는데, 파일 권한에 s 비트가 있어 소유자인 root 권한으로 실행되기 때문입니다. root 소유 setuid 파일은 이렇게 찾을 수 있습니다:

find /bin -user root -perm -u+s

또 하나의 실전 팁: /etc/sudoers에 sudo echo ... >> /etc/sudoers처럼 redirect로 쓰면 실패합니다. echo만 root로 실행되고 append(>>)는 현재 사용자 권한으로 수행되기 때문입니다. sudo tee -a를 쓰거나, 가급적 sudo visudo로 편집하세요. visudo는 저장 전에 문법을 검증해 sudo가 먹통이 되는 사고를 막아줍니다.

프로세스 상태 코드: R, S, D, Z, T

htop의 S 열에 표시되는 상태 코드는 트러블슈팅의 핵심입니다.

| 코드 | 의미 | |------|------| | R | 실행 중이거나 run queue에서 대기 중 | | S | interruptible sleep — 이벤트를 기다리는 중 | | D | uninterruptible sleep — 보통 I/O 대기 | | Z | 좀비 — 종료됐지만 parent가 reap하지 않음 | | T | job control signal로 정지 | | t | debugger에 의해 정지 |

S: signal로 깨울 수 있는 잠

sleep 1000 같은 프로세스가 여기 해당합니다. kill은 사실 signal을 보내는 시스템 호출이며, 기본 signal은 TERM(종료 요청)입니다. CTRL+C는 bash가 foreground 프로세스에 SIGINT를 보내는 것이고요. 프로그램이 signal handler를 등록했다면 SIGTERM을 받고도 안 죽을 수 있습니다. SIGKILL(9)만이 커널이 프로세스에 응답 기회 없이 강제 종료하는 signal입니다.

D: SIGKILL도 안 통하는 상태

D 상태는 signal로 깨울 수 없습니다. SIGKILL도 signal이기 때문에 kill -9로도 죽일 수 없습니다. 보통 디스크 I/O나 NFS 지연, 메모리 부족으로 인한 과도한 swap 상황에서 나타납니다. 예를 들어 응답 없는 NFS 서버에 mount를 시도하면 /sbin/mount.nfs가 D 상태에 빠집니다. NFS라면 -o intr 옵션으로 interruptible하게 마운트할 수 있습니다.

Z: 좀비는 죽일 수 없다

좀비 프로세스는 이미 종료된 프로세스입니다. 메모리도 안 쓰고 PID만 차지하며, parent가 wait으로 reap해주기를 기다리는 상태죠. 이미 죽었으니 kill이 통하지 않습니다. 잠깐 존재하는 좀비는 정상이지만, 오래 남아 있다면 parent 프로그램의 버그 신호입니다.

VIRT, RES, SHR: 메모리 숫자 세 개의 진실

htop의 메모리 열은 각각 다른 관점을 보여줍니다:

  • VIRT — 가상 메모리. 프로세스가 "예약"한 주소 공간으로, 실제 사용량과 거리가 멉니다
  • RES — 물리 메모리(RAM)에 실제로 올라간 양
  • SHR — 다른 프로세스와 공유 가능한 메모리 (공유 라이브러리 등)
  • MEM% — RES 기준 비율

같은 라이브러리를 쓰는 프로세스 여러 개의 RES를 단순 합산하면 공유 메모리가 중복 계산됩니다. 즉, 한 숫자만 보고 실제 메모리 사용량을 단정하기 어렵다는 점을 기억하세요.

마치며

htop은 결국 /proc이라는 창을 예쁘게 보여주는 도구입니다. 화면의 숫자가 이상해 보일 때 strace로 어떤 파일을 읽는지 추적하고, /proc/<pid>/ 아래를 직접 열어보는 습관을 들이면 서버 트러블슈팅 실력이 확실히 한 단계 올라갑니다. 특히 "load는 높은데 CPU는 놀고 있는" 상황에서 D 상태 프로세스를 찾아보는 것, 오늘부터 바로 써먹을 수 있는 팁입니다.


원문: https://news.hada.io/topic?id=31136