Skip to content

Split View: htop와 top 완벽 해부 — 모든 숫자를 읽는 법

|

htop와 top 완벽 해부 — 모든 숫자를 읽는 법

들어가며 — 숫자의 홍수

리눅스 서버에서 htop이나 top을 처음 열면 숫자와 색깔의 홍수에 압도됩니다. 대부분은 CPU와 메모리 퍼센트만 대충 보고 나머지는 무시하지만, 그 숫자 하나하나에는 시스템의 상태를 진단하는 데 필요한 정보가 담겨 있습니다. 최근 해커뉴스(Hacker News) 상단에 다시 오른 페테리스 니키포로프스(Pēteris Ņikiforovs)의 명문 해설 "Explanation of everything you can see in htop"이 이 주제를 다시 조명한 것을 계기로, 이 글에서는 그 필드들을 하나씩 뜯어보겠습니다.

프로세스 열 읽기

프로세스 목록의 각 열이 무엇을 뜻하는지부터 봅시다.

  • PID: 프로세스 ID. 프로세스가 시작될 때 부여되는 고유 번호입니다.
  • USER: 프로세스의 소유자. 그 프로세스를 실행한 사용자입니다.
  • PRINI: 우선순위(priority)와 나이스(nice) 값입니다. 관계는 PR = 20 + NI 로 표현됩니다. 나이스 값은 -20(가장 높은 우선순위)부터 19(가장 낮은 우선순위)까지이며, 나이스가 낮을수록 CPU를 더 많이 배정받습니다. 대략적인 어림으로, 나이스 한 단계마다 약 10%의 CPU 시간이 이동한다고 봅니다.
  • VIRT / RES / SHR: 메모리 관련 세 값이며 아래에서 따로 다룹니다.
  • S: 프로세스 상태(state)이며 역시 아래에서 다룹니다.
  • %CPU: CPU 사용률입니다.
  • %MEM: RES / 전체 RAM 로 계산되는 메모리 사용률입니다.
  • TIME+: 프로세스가 지금까지 누적으로 사용한 CPU 시간입니다.

VIRT, RES, SHR — 메모리 세 값의 함정

메모리 열은 가장 자주 오해되는 부분입니다. 세 값의 의미가 완전히 다릅니다.

  • VIRT (가상 메모리): 프로세스가 확보한 전체 가상 주소 공간입니다. 여기에는 실제로 쓰지 않는 영역, 공유 라이브러리, 메모리 맵 파일, 스왑으로 밀려난 페이지까지 모두 포함됩니다. 그래서 VIRT는 대개 부풀려져 있고, 실제 메모리 사용량의 지표로는 거의 쓸모가 없습니다.
  • RES (상주 메모리): 스왑되지 않고 실제 물리 RAM에 올라와 있는, 프로세스가 실제로 쓰고 있는 메모리입니다. "이 프로세스가 지금 RAM을 얼마나 먹고 있나"를 알고 싶다면 이 값을 봐야 합니다.
  • SHR (공유 메모리): 다른 프로세스와 공유될 수 있는 메모리입니다. 대표적으로 공유 라이브러리가 여기에 해당합니다.

여기서 중요한 실무 교훈이 하나 나옵니다. 메모리를 많이 먹는 프로세스를 찾을 때는 VIRT가 아니라 RES 기준으로 정렬해야 한다는 것입니다. VIRT로 정렬하면 실제로는 메모리를 별로 안 쓰는데 주소 공간만 크게 잡은 프로세스가 맨 위에 올라와 오판하기 쉽습니다.

프로세스 상태 문자

S 열에는 한 글자로 된 프로세스 상태가 표시됩니다. 각 문자의 의미는 다음과 같습니다.

  • R — 실행 중이거나 실행 대기(runnable). CPU에서 돌고 있거나 차례를 기다리는 상태입니다.
  • S — 인터럽트 가능한 수면(interruptible sleep). 어떤 이벤트를 기다리며 잠들어 있지만 시그널로 깨울 수 있습니다. 평상시 대부분의 프로세스가 이 상태입니다.
  • D — 인터럽트 불가능한 수면(uninterruptible sleep). 보통 디스크 입출력을 기다리는 상태이며, 시그널로도 깨울 수 없습니다.
  • Z — 좀비(zombie). 종료되었지만 부모가 아직 거둬가지 않은 상태입니다.
  • T — 작업 제어 시그널로 정지된 상태(예: Ctrl+Z).
  • t — 디버거/트레이서에 의해 정지된 상태입니다.

로드 애버리지 — CPU 사용률과 다르다

가장 널리 오해되는 지표가 로드 애버리지(load average) 입니다. 흔히 "CPU 사용률"과 같다고 생각하지만 그렇지 않습니다.

첫째, 로드 애버리지는 단순 평균이 아니라 지수 감쇠 이동 평균입니다. 1분, 5분, 15분 세 개의 값이 표시되며, 최근 값에 더 큰 가중치를 둡니다. 1분 값은 대략 지난 1분의 부하를 63%, 그 이전을 37% 정도로 섞은 값입니다.

둘째, 그리고 더 중요한 점 — 로드는 실행 중인(R) 프로세스뿐 아니라 인터럽트 불가능한 수면(D) 상태의 프로세스도 함께 셉니다. 이 때문에 로드가 높다고 반드시 CPU가 바쁜 것은 아닙니다. 디스크나 네트워크 입출력을 기다리는 프로세스가 많아도 로드는 치솟습니다.

셋째, 로드는 코어 수를 기준으로 해석해야 합니다. N개의 코어를 가진 머신에서 로드가 N이면 대략 100% 활용 상태입니다. 코어가 4개인데 로드가 4라면 포화 상태이고, 코어가 8개인데 로드가 4라면 절반만 쓰이는 셈입니다.

이 세 가지를 종합하면 강력한 진단 규칙이 나옵니다. 로드는 높은데 %CPU는 낮다면, 십중팔구 입출력 대기(I/O wait) 입니다. 이때는 D 상태의 프로세스를 찾아보는 것이 문제 해결의 출발점입니다.

미터의 색깔

htop 상단의 막대 미터도 색으로 정보를 전합니다.

  • CPU 미터: 파란색은 낮은 우선순위(나이스 값이 양수인) 스레드, 초록색은 일반 우선순위 스레드, 빨간색은 커널 스레드를 나타냅니다. (htop은 이 밖에도 가상화나 입출력 대기 등을 더 세분해 표시하기도 합니다.)
  • 메모리 미터: 초록색은 실제로 쓰이는 메모리(used), 파란색은 버퍼(buffers), 주황색은 캐시(cache)입니다.

여기서 버퍼와 캐시를 "쓰이는 메모리"로 착각하지 않는 것이 중요합니다. 버퍼와 캐시는 커널이 성능을 위해 잠시 빌려 쓰는 영역이며, 애플리케이션이 메모리를 필요로 하면 즉시 반납됩니다. 그래서 실제로 주목해야 할 값은 "used"가 아니라 가용 메모리(available) 입니다. 리눅스에서 "메모리가 거의 다 찼다"는 화면이 대개 문제가 아닌 이유가 이것입니다. 캐시로 채워진 메모리는 필요할 때 비워질 수 있는, 사실상 여유 메모리입니다.

좀비 프로세스의 정체

좀비(Z) 프로세스는 자주 오해됩니다. 몇 가지 사실을 정리하겠습니다.

좀비는 이미 죽은 프로세스입니다. 실행을 마쳤지만, 부모 프로세스가 그 종료 상태를 아직 거둬가지(reap) 않아 프로세스 테이블에 항목만 남아 있는 상태입니다. 그래서 좀비는 메모리를 전혀 소비하지 않습니다. 오직 프로세스 테이블의 슬롯 하나만 차지할 뿐입니다.

또한 좀비는 죽일 수 없습니다. 프로세스를 종료시키려면 시그널을 보내야 하는데, 시그널은 살아 있는 프로세스만 받을 수 있습니다. 이미 죽은 좀비에게 kill -9를 보내도 소용이 없습니다. 좀비가 사라지는 유일한 길은 부모가 그것을 거둬가는 것입니다. 만약 부모가 먼저 죽으면, 좀비는 init(PID 1)에게 입양되고 init이 대신 거둬갑니다.

즉 좀비가 몇 개 보인다고 당황할 필요는 없습니다. 좀비가 계속 쌓인다면 그것은 좀비 자체가 문제라기보다, 자식을 제대로 거둬가지 않는 부모 프로세스의 버그를 가리키는 신호입니다.

카피 온 라이트와 RES의 착시

한 가지 미묘한 현상을 덧붙이겠습니다. 프로세스가 fork로 자식을 만들면, 리눅스는 카피 온 라이트(copy-on-write) 를 씁니다. 부모와 자식이 같은 물리 메모리 페이지를 공유하다가, 어느 쪽이 쓰기를 할 때에만 그 페이지를 복제합니다.

이 때문에 htop에서 부모와 자식이 각각 전체 RES를 통째로 표시할 수 있습니다. 겉보기에는 메모리를 두 배로 쓰는 것 같지만, 실제 물리 메모리는 공유되고 있어 합계가 그만큼 크지 않습니다. 프로세스를 많이 fork하는 프로그램의 메모리를 볼 때 이 착시를 염두에 두어야 합니다.

실전 진단 레시피

지금까지의 내용을 실제 문제 해결에 적용하면 다음과 같습니다.

  • "로드는 높은데 CPU는 한가하다" → 입출력 대기입니다. D 상태 프로세스를 찾고, 디스크나 네트워크 병목을 의심합니다.
  • 메모리를 먹는 범인 찾기 → VIRT가 아니라 RES 기준으로 정렬합니다.
  • 폭주하는 스레드 찾기 → htop에서 스레드 표시를 켜고 %CPU가 지속적으로 높은 항목을 봅니다.
  • "메모리가 꽉 찼다" → used가 아니라 available을 봅니다. 캐시로 찬 것은 문제가 아닙니다.

이 도구들을 브라우저에서 직접 만져보고 싶다면 이 사이트의 리눅스 터미널이나 웹 터미널을, 커널 파라미터가 이런 동작에 어떻게 관여하는지 살펴보고 싶다면 커널 파라미터 탐색기를 함께 열어보면 좋습니다.

마치며

htop과 top의 숫자들은 처음엔 암호처럼 보이지만, 각 필드의 의미를 알고 나면 시스템의 상태를 읽는 강력한 진단 도구가 됩니다. 특히 VIRT와 RES의 차이, 로드가 입출력 대기를 포함한다는 사실, 캐시가 사실상 여유 메모리라는 점 — 이 세 가지만 제대로 이해해도 서버 문제 진단의 절반은 해결됩니다.

참고 자료

Reading htop and top — How to Decode Every Number

Introduction — A Flood of Numbers

Open htop or top on a Linux server for the first time and you are overwhelmed by a flood of numbers and colors. Most people glance at the CPU and memory percentages and ignore the rest, but each of those numbers holds information you need to diagnose the state of the system. Prompted by Pēteris Ņikiforovs's beloved explainer "Explanation of everything you can see in htop" recently resurfacing near the top of Hacker News, this article takes those fields apart one by one.

Reading the Process Columns

Let us start with what each column in the process list means.

  • PID: the process ID, a unique number assigned when a process starts.
  • USER: the owner of the process — the user who launched it.
  • PRI and NI: priority and nice value. The relationship is PR = 20 + NI. Nice ranges from -20 (highest priority) to 19 (lowest priority); the lower the nice, the more CPU the process gets. As a rough heuristic, each nice step shifts about 10% of CPU time.
  • VIRT / RES / SHR: three memory values, covered separately below.
  • S: the process state, also covered below.
  • %CPU: CPU usage.
  • %MEM: memory usage, computed as RES / total RAM.
  • TIME+: the cumulative CPU time the process has used so far.

VIRT, RES, SHR — The Trap of the Three Memory Values

The memory columns are the most commonly misunderstood part. The three values mean completely different things.

  • VIRT (virtual memory): the entire virtual address space the process has reserved. This includes regions it never actually uses, shared libraries, memory-mapped files, and pages swapped out to disk. So VIRT is usually inflated and nearly useless as an indicator of real memory usage.
  • RES (resident memory): the non-swapped physical RAM the process is actually using. If you want to know "how much RAM is this process eating right now," this is the value to look at.
  • SHR (shared memory): memory that could be shared with other processes — shared libraries are the classic example.

An important practical lesson follows: when hunting for a memory-hungry process, sort by RES, not VIRT. Sort by VIRT and a process that reserved a large address space but barely uses memory floats to the top, leading you astray.

The Process State Letters

The S column shows a one-letter process state. The letters mean:

  • R — running or runnable. Executing on a CPU or waiting its turn.
  • S — interruptible sleep. Sleeping while waiting for an event, but wakeable by a signal. Most processes are in this state normally.
  • D — uninterruptible sleep. Usually blocked on disk I/O, and cannot be woken even by a signal.
  • Z — zombie. Terminated but not yet reaped by its parent.
  • T — stopped by a job-control signal (e.g. Ctrl+Z).
  • t — stopped by a debugger/tracer.

Load Average — It Is Not CPU Usage

The most widely misunderstood metric is the load average. People often think it equals "CPU usage," but it does not.

First, load average is not a simple average but an exponentially damped moving average. Three values are shown — over 1, 5, and 15 minutes — weighting recent values more heavily. The 1-minute figure is roughly 63% of the last minute's load plus 37% of what came before.

Second, and more important — load counts not only running (R) processes but also processes in uninterruptible sleep (the D state). Because of this, a high load does not necessarily mean the CPU is busy. Load spikes even when many processes are waiting on disk or network I/O.

Third, load must be interpreted against the core count. On a machine with N cores, a load of N means roughly full utilization. Load 4 on a 4-core box is saturation; load 4 on an 8-core box is only half utilized.

Combining these gives a powerful diagnostic rule: if load is high but %CPU is low, it is almost certainly I/O wait. The place to start is looking for D-state processes.

The Meter Colors

The bar meters at the top of htop convey information through color too.

  • CPU meter: blue for low-priority (positively niced) threads, green for normal-priority threads, red for kernel threads. (htop also breaks out categories like virtualization and I/O wait in finer detail.)
  • Memory meter: green for used memory, blue for buffers, orange for cache.

The key is not to mistake buffers and cache for "used memory." Buffers and cache are regions the kernel borrows temporarily for performance, and they are returned immediately when an application needs memory. So the value to watch is not "used" but available memory. This is why a Linux screen showing "memory almost full" is usually not a problem — memory filled by cache is, in effect, free memory that can be reclaimed on demand.

What Zombie Processes Really Are

Zombie (Z) processes are often misunderstood. A few facts:

A zombie is an already-dead process. It has finished executing, but its parent has not yet reaped its exit status, so only an entry remains in the process table. Because of this, a zombie consumes no memory at all — it occupies only a single slot in the process table.

Also, a zombie cannot be killed. To terminate a process you send it a signal, and only a live process can receive a signal. Sending kill -9 to a zombie that is already dead does nothing. The only way a zombie disappears is for its parent to reap it. If the parent dies first, the zombie is adopted by init (PID 1), which reaps it instead.

So there is no need to panic over a few zombies. If zombies keep piling up, that points less to the zombies themselves being a problem than to a bug in a parent process that fails to reap its children.

Copy-on-Write and the RES Illusion

One subtle phenomenon to add. When a process creates a child with fork, Linux uses copy-on-write. Parent and child share the same physical memory pages, and a page is copied only when one of them writes to it.

Because of this, htop can show both parent and child with the full RES each. It looks like double the memory, but the physical memory is shared, so the total is not that large. Keep this illusion in mind when looking at the memory of a program that forks heavily.

Practical Diagnostic Recipes

Applying all this to real troubleshooting:

  • "Load is high but the CPU is idle" → I/O wait. Look for D-state processes and suspect a disk or network bottleneck.
  • Finding the memory culprit → sort by RES, not VIRT.
  • Finding a runaway thread → turn on thread display in htop and watch for an item with persistently high %CPU.
  • "Memory is full" → look at available, not used. Memory filled by cache is not a problem.

If you want to get hands-on with these tools in the browser, open this site's Linux terminal or web terminal, and to explore how kernel parameters bear on this behavior, the kernel parameter explorer.

Conclusion

The numbers in htop and top look like a cipher at first, but once you know what each field means they become a powerful diagnostic tool for reading the state of a system. In particular, understanding just three things — the difference between VIRT and RES, that load includes I/O wait, and that cache is effectively free memory — already solves half of server troubleshooting.

References