---
title: "리눅스 htop/top에 보이는 숫자들, 대충 보지 말고 제대로 읽기"
published: 2026-07-04T12:00:57.000Z
canonical: https://jeff.news/article/4625
---
# 리눅스 htop/top에 보이는 숫자들, 대충 보지 말고 제대로 읽기

htop과 top에 나오는 업타임, 로드 애버리지, 프로세스 상태, 메모리 지표가 실제로 어디서 오고 무엇을 뜻하는지 풀어낸 긴 해설이다. /proc 파일시스템, strace, fork/exec/wait, signal, niceness, VIRT/RES/SHR 같은 기본기를 실제 명령 예제로 연결해 보여준다. 2019년 글이지만 리눅스 서버를 만지는 개발자에게 여전히 실무 가치가 높다.

## htop 숫자는 그냥 UI가 아니라 커널 상태를 읽은 결과임

- htop/top에 보이는 값 대부분은 리눅스 커널이 노출한 정보를 사람이 보기 좋게 바꾼 것임
  - 예를 들어 uptime은 `/proc/uptime`을 읽고, load average는 `/proc/loadavg`를 읽음
  - 작성자는 `strace uptime`으로 uptime 프로그램이 실제로 어떤 파일을 여는지 확인함
  - `/proc/uptime`의 첫 번째 숫자는 시스템이 켜져 있던 전체 초, 두 번째 숫자는 idle 시간인데 멀티코어에서는 코어별 idle 합산이라 전체 uptime보다 커질 수 있음

- 그래서 htop을 제대로 읽으려면 `/proc`를 같이 이해해야 함
  - `/proc/<pid>/cmdline`은 프로세스를 실행한 명령을 보여줌
  - `/proc/<pid>/cwd`는 현재 작업 디렉터리, `/proc/<pid>/exe`는 실행 바이너리로 연결되는 심볼릭 링크임
  - htop, top, ps 같은 도구는 결국 이런 커널 노출 정보를 조합해서 보여주는 뷰어에 가까움

## Load average는 CPU 사용률이 아님

- 많은 사람이 load average 1.0을 “CPU 100%”나 “2코어면 50%”처럼 외우는데, 이건 반만 맞음
  - load average는 실행 중이거나 실행 대기 중인 프로세스, 그리고 uninterruptible 상태의 프로세스 수를 기반으로 계산됨
  - 1분, 5분, 15분 값은 단순 구간 평균이 아니라 지수적으로 감쇠되는 이동 평균임
  - 위키피디아 기준으로 1분 load average도 최근 1분 활동을 많이 반영할 뿐, 과거 값이 완전히 사라지는 구조는 아님

- CPU bound 작업 하나를 1코어 머신에서 돌리면 load average가 1.00 근처로 올라가고 CPU는 사실상 꽉 참
  - 같은 상황에서 2코어 머신이면 load 1.00은 대략 한 코어만 바쁜 상태로 볼 수 있음
  - 하지만 D 상태 같은 I/O 대기 프로세스도 load에 들어가서, CPU는 한가한데 load만 높은 상황도 가능함
  - 그래서 순간 CPU 사용률은 `mpstat 1` 같은 도구로 따로 보는 게 더 정확함

> [!IMPORTANT]
> load average는 “CPU가 몇 퍼센트 바쁜가”가 아니라 “CPU나 I/O 때문에 처리 대기 중인 일이 얼마나 쌓였나”에 가깝다. 코어 수, I/O 대기, 시간 흐름을 같이 봐야 숫자가 의미를 갖는다.

## 프로세스는 PID 하나로 끝나지 않음

- 리눅스 커널은 프로세스를 task라고도 부르고, htop의 Tasks 표기도 여기서 나옴
  - 프로세스마다 PID가 있고, 새 프로세스가 뜰 때 보통 증가하는 ID를 받음
  - PID 1은 부팅 시 시작되는 init 또는 systemd가 차지함
  - 스레드는 htop에서 Shift+H, 커널 스레드는 Shift+K로 표시 여부를 바꿀 수 있음

- 프로세스는 부모와 자식 관계를 만들고, 이 구조가 트리로 이어짐
  - bash에서 `date`를 실행하면 bash가 fork로 자기 복사본을 만들고, exec로 `/bin/date`를 메모리에 올림
  - 부모 bash는 자식 프로세스가 끝날 때까지 wait 계열 호출로 기다림
  - htop에서 F5를 누르면 이 부모-자식 계층을 트리로 볼 수 있음

```mermaid
sequenceDiagram
    participant 셸
    participant 커널
    participant 자식프로세스
    participant 실행파일
    셸->>커널: fork 호출
    커널-->>자식프로세스: 셸 복사본 생성
    자식프로세스->>커널: exec 호출
    커널->>실행파일: 바이너리 로드
    셸->>커널: wait 호출
    자식프로세스-->>커널: 종료 코드 반환
    커널-->>셸: 자식 종료 상태 전달
```

## R, S, D, Z 상태는 장애 분석의 기본 언어임

- R은 실행 중이거나 실행 대기 중인 상태임
  - CPU가 실제로 명령을 실행 중이거나, 실행할 차례를 기다리는 run queue에 있다는 뜻임
  - `cat /dev/urandom > /dev/null` 같은 CPU를 계속 쓰는 작업을 돌리면 R 상태와 높은 CPU 사용률을 보기 쉬움

- S는 interruptible sleep, 즉 이벤트를 기다리는 일반적인 대기 상태임
  - `sleep 1000`은 CPU를 쓰는 게 아니라 시간이 지나길 기다리므로 S 상태로 보임
  - SIGINT, SIGTERM 같은 signal을 보내면 깨어나거나 종료될 수 있음
  - CTRL+C는 foreground 프로세스에 SIGINT를 보내는 동작임

- D는 uninterruptible sleep이라서 운영 중에 보면 꽤 찝찝한 상태임
  - 보통 디스크나 네트워크 파일시스템 I/O를 기다릴 때 잠깐 나타남
  - signal로 깨울 수 없어서 `kill -9`도 바로 먹히지 않음
  - NFS가 멈췄거나 swap이 심하게 발생하는 상황에서 오래 보이면 원인 추적이 필요함

- Z는 좀비 프로세스, 이미 종료됐지만 부모가 아직 회수하지 않은 상태임
  - 메모리는 거의 안 먹고 PID 정도만 차지함
  - 짧게 생겼다 사라지는 건 정상임
  - 오래 남아 있으면 부모 프로세스가 wait 처리를 제대로 안 하는 버그일 수 있음

> [!WARNING]
> D 상태 프로세스는 “강제 종료하면 되겠지”가 잘 안 통한다. 대개 그 프로세스가 기다리는 I/O 원인을 찾아야 풀린다.

## VIRT, RES, SHR은 전부 다른 메모리 숫자임

- VIRT는 프로세스가 가진 가상 메모리 전체라서 대개 과장돼 보임
  - 1GB를 요청만 하고 실제로 1MB만 써도 VIRT는 1GB처럼 보일 수 있음
  - mmap으로 큰 파일을 매핑만 해도 VIRT가 크게 잡힘
  - 그래서 평소 장애 판단에는 단독으로 믿기 애매한 숫자임

- RES는 실제 물리 메모리에 올라와 있는 non-swapped 메모리라 VIRT보다 현실적인 지표임
  - 다만 swap으로 빠진 메모리는 빠져 있음
  - 공유 메모리까지 포함될 수 있어 프로세스별 RES를 단순 합산하면 실제 사용량보다 커질 수 있음
  - fork 이후 Copy-on-Write 때문에 부모와 자식 RES가 각각 크게 보여도 물리 메모리는 공유될 수 있음

- SHR은 다른 프로세스와 공유될 수 있는 메모리 크기임
  - 공유 라이브러리나 공유 메모리 영역 때문에 생김
  - “이 프로세스만 독점적으로 먹는 메모리”를 알고 싶다면 RES와 SHR의 의미 차이를 같이 봐야 함
  - MEM%는 대체로 RES를 전체 RAM으로 나눈 비율임

## 우선순위와 niceness는 CPU 경합 때 의미가 커짐

- CPU 코어보다 실행할 작업이 많으면 커널 스케줄러가 다음에 뭘 실행할지 골라야 함
  - 사용자가 직접 커널 스케줄러를 마음대로 조종하긴 어렵지만, niceness로 힌트를 줄 수 있음
  - NI 값은 -20부터 19까지이며, -20이 높은 우선순위, 19가 낮은 우선순위임
  - 이름 때문에 헷갈리지만 “더 nice한 프로세스는 더 많이 양보한다”고 보면 됨

- PRI는 커널이 쓰는 우선순위 값이고, 일반 사용자 프로세스 영역은 보통 100-139에 매핑됨
  - 글에서는 `PR = 20 + NI` 관계를 통해 nice 값이 커널 우선순위에 어떻게 반영되는지 설명함
  - 실행 전에는 `nice -n`, 실행 중에는 `renice -n -p PID`로 조정할 수 있음
  - htop CPU 바의 색도 low priority thread, normal thread, kernel thread 등을 구분해서 보여줌

---

## 기술 맥락

- 이 글의 핵심 선택은 htop 화면을 외워서 설명하지 않고, 각 값이 어디서 오는지 `/proc`와 `strace`로 역추적한 거예요. 그래서 단순 사용법이 아니라 리눅스 관찰 도구들이 커널과 어떻게 연결되는지 감이 생겨요.

- load average를 CPU 사용률로 뭉개지 않은 것도 중요해요. 실무 장애에서는 CPU가 한가한데 load가 치솟는 경우가 있고, 그때 D 상태 I/O 대기나 NFS, swap 같은 쪽을 봐야 하거든요.

- 프로세스 상태 설명도 명령어와 연결돼 있어서 좋아요. R, S, D, Z를 표로 외우는 대신 `sleep`, `kill`, `fork`, `wait`, NFS mount 예제로 확인하니 운영 중에 htop을 봤을 때 바로 해석할 수 있어요.

- 메모리 지표는 특히 오해가 많아요. VIRT가 크다고 메모리 누수라고 단정하면 안 되고, RES도 공유 메모리와 Copy-on-Write 때문에 합산하면 틀릴 수 있어요. 그래서 리눅스 서버에서는 숫자 하나보다 커널이 어떤 기준으로 그 숫자를 만들었는지를 아는 게 더 중요해요.

## 핵심 포인트

- 로드 애버리지는 CPU 사용률 그 자체가 아니라 실행 중이거나 실행 대기 중인 프로세스와 uninterruptible 상태 프로세스의 지수 이동 평균이다
- htop, top, ps 같은 도구는 대부분 /proc 아래의 커널 노출 정보를 읽어서 사람이 보기 좋게 보여준다
- 프로세스 상태 R, S, D, Z, T, t는 장애 분석에서 바로 쓰이는 신호이며 특히 D 상태와 좀비 프로세스는 오해가 많다
- VIRT, RES, SHR, MEM%는 각각 다른 의미라서 메모리 사용량을 단일 숫자로 판단하면 쉽게 헷갈린다

## 인사이트

htop을 ‘CPU 많이 먹는 프로세스 찾는 화면’ 정도로만 쓰면 장애 때 놓치는 게 많다. 이 글의 가치는 각 숫자가 어떤 커널 인터페이스에서 왔는지까지 연결해, 운영 감각을 한 단계 낮은 레이어로 내려준다는 데 있다.
