사이트 접속이 갑자기 느려지고, SSH로 들어가는 것조차 버벅일 때가 있습니다. 이럴 때 많은 분들이 먼저 디스크부터 확인합니다. 물론 디스크가 꽉 차서 생기는 문제도 흔합니다. 하지만 디스크는 멀쩡한데 CPU가 100%에 붙어 있거나, 메모리를 다 먹어서 스왑으로 밀려나 있는 경우도 그만큼 많습니다.
이때 봐야 할 게 프로세스입니다. 프로세스는 지금 서버에서 돌아가고 있는 프로그램 하나하나를 말합니다. nginx도 프로세스고, php-fpm도, mysqld도, 심지어 여러분이 방금 친 ps 명령도 프로세스입니다. 각각에는 PID(Process ID)라는 고유 번호가 붙습니다. 프로세스를 다루는 명령은 거의 다 이 숫자를 기준으로 움직입니다.
한눈에 ·
whoami로 권한 확인 →ps aux로 목록 뽑기 →top으로 실시간 감시 →kill PID(15) 먼저, 안 되면kill -9→ps로 종료 확인 → 로그와 OOM 흔적 남기기
ps와 top은 역할이 다릅니다. ps는 명령을 친 그 순간의 사진 한 장입니다. 찍고 끝납니다. top은 계속 갱신되는 움직이는 화면입니다. 순간적으로 튀는 프로세스를 잡으려면 top이 낫고, 목록을 파일로 저장하거나 다른 명령과 조합하려면 ps가 낫습니다. 아래에서는 찾기 → 확인 → 종료 → 재발 방지 순서로 갑니다.
시작 전에 알아둘 것: 내 권한과 대상 서버 확인
프로세스가 뭔지 감을 잡았으면, 명령을 치기 전에 딱 하나만 확인하고 갑니다. 지금 내가 누구인지입니다. 이걸 건너뛰면 뒤에서 “Operation not permitted” 에러를 보고 한참 헤매게 됩니다.
whoami
id
whoami는 현재 로그인한 계정 이름을 한 줄로 보여줍니다. id는 UID와 소속 그룹까지 같이 보여줍니다. uid=0(root)이면 root, uid=1000(ubuntu) 같은 형태면 일반 계정입니다.
일반 계정과 root의 차이
리눅스에서 프로세스를 종료할 권한은 소유자에게 있습니다. 내가 실행한 프로세스는 내가 죽일 수 있습니다. 하지만 root가 띄운 nginx 마스터 프로세스는 일반 계정이 건드릴 수 없습니다. sudo를 앞에 붙여야 합니다.
남의 프로세스를 종료하려면 sudo가 필요하다는 것만 기억하면 됩니다. 다만 sudo를 습관적으로 붙이는 건 권하지 않습니다. 내 프로세스만 정리하면 되는 상황에서 sudo를 쓰다가 엉뚱한 걸 죽이는 사고가 납니다.
공유 호스팅에서 제한되는 경우
카페24, 가비아 같은 공유 웹호스팅 계정에서는 상황이 다릅니다. 이런 환경은 /proc을 hidepid 옵션으로 마운트해서 다른 사용자의 프로세스를 아예 안 보여주는 경우가 있습니다. ps aux를 쳐도 내 계정 프로세스 몇 개만 나옵니다.
이건 고장이 아니라 의도된 설정입니다. 이 경우 서버 전체 부하는 확인할 방법이 없고, 호스팅사 관리 패널의 리소스 그래프를 보거나 문의하는 수밖에 없습니다. 제한 방식은 호스팅사마다 달라서 일괄로 말하기 어렵습니다.
ps로 프로세스 목록 뽑기: aux와 -ef, 그리고 grep 조합
권한 확인이 끝났으면 이제 목록부터 뽑습니다. 가장 많이 쓰는 조합은 ps aux입니다. 하이픈이 없는 게 맞습니다. BSD 스타일 옵션이라 그렇습니다.
ps aux
ps aux 출력 열 읽는 법
실제 출력은 이런 모양입니다.
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 168404 11556 ? Ss 09:12 0:03 /sbin/init
www-data 2841 12.4 8.2 412300 84120 ? S 10:31 1:22 php-fpm: pool www
mysql 1502 3.1 22.7 189244 229104 ? Ssl 09:13 4:15 /usr/sbin/mysqld
ubuntu 3920 0.0 0.0 13140 1044 pts/0 R+ 11:04 0:00 ps aux
열이 많아 보이지만 실제로 보는 건 몇 개 안 됩니다.
| 열 | 의미 | 볼 때 포인트 |
|---|---|---|
| USER | 프로세스 소유자 | 내 계정이 아니면 sudo 필요 |
| PID | 프로세스 번호 | kill에 넣을 숫자 |
| %CPU | CPU 점유율 | 100 넘으면 멀티코어 합산값 |
| %MEM | 물리 메모리 점유율 | 전체 램 대비 비율 |
| RSS | 실제 사용 중인 메모리(KB) | %MEM보다 정확한 절대값 |
| STAT | 프로세스 상태 | Z나 D면 문제 신호 |
| COMMAND | 실행된 명령 전체 | 어떤 프로그램인지 식별 |
STAT 열은 나중에 중요해지니 여기서 정리하고 갑니다.
| STAT | 상태 | 설명 |
|---|---|---|
| R | Running | 실행 중이거나 실행 대기 |
| S | Sleeping | 뭔가를 기다리는 중, 대부분 정상 |
| D | Uninterruptible sleep | 디스크·NFS I/O 대기, kill이 안 먹힘 |
| Z | Zombie | 이미 죽었는데 부모가 정리 안 함 |
| T | Stopped | 정지됨(Ctrl+Z 등) |
뒤에 붙는 s는 세션 리더, l은 멀티스레드, +는 포그라운드를 뜻합니다.
ps -ef도 자주 보입니다. 이건 UNIX 스타일 옵션이고, 출력 열이 조금 다릅니다(%CPU 대신 PPID, 부모 프로세스 번호가 나옵니다). 부모 자식 관계를 봐야 할 때 유용합니다. 둘 중 어느 쪽을 쓸지는 취향 문제에 가깝습니다.
ps -ef
특정 프로세스만 찾기
전체 목록은 수백 줄이라 눈으로 못 봅니다. grep으로 걸러냅니다.
ps aux | grep nginx
그런데 여기서 처음 하는 분들이 꼭 걸리는 게 있습니다. 결과에 grep --color=auto nginx라는 줄이 하나 더 나옵니다. grep 명령 자체도 프로세스라서 자기 자신이 목록에 잡힌 겁니다. 이걸 빼려면 한 번 더 거릅니다.
ps aux | grep nginx | grep -v grep
grep -v는 해당 문자열이 든 줄을 제외한다는 뜻입니다. grep 옵션을 더 파고들려면 grep으로 로그에서 원하는 줄만 뽑기, 실전 옵션 정리를 같이 보시면 됩니다.
더 깔끔한 방법은 pgrep입니다. 처음부터 프로세스 검색 전용 명령이라 자기 자신이 안 잡힙니다.
pgrep -a nginx
-a는 PID와 함께 전체 명령줄까지 보여주는 옵션입니다. PID만 필요하면 -a를 빼면 됩니다.
CPU·메모리 순 정렬
범인을 찾을 때 가장 빠른 건 정렬입니다.
ps aux --sort=-%mem | head -10
ps aux --sort=-%cpu | head -10
--sort 앞의 마이너스는 내림차순이라는 뜻입니다. head -10으로 상위 10줄만 잘라 봅니다. 첫 줄은 헤더라 실제로는 9개가 보입니다.

top으로 실시간 감시하기: 화면 읽는 법과 단축키
ps가 한 장의 사진이라면, 계속 변하는 상황은 top으로 봅니다. 부하가 몇 초씩 튀었다 가라앉는 경우엔 ps로 잡기 어렵습니다.
top
load average는 어떻게 읽나
top을 실행하면 화면 위쪽에 요약 정보가 나옵니다. 이 중 load average가 핵심입니다.
top - 11:07:32 up 3 days, 2:14, 1 user, load average: 2.35, 1.88, 1.42
Tasks: 142 total, 2 running, 140 sleeping, 0 stopped, 0 zombie
%Cpu(s): 34.2 us, 8.1 sy, 0.0 ni, 55.3 id, 2.1 wa, 0.0 hi, 0.3 si
MiB Mem : 3936.4 total, 212.8 free, 2891.1 used, 832.5 buff/cache
MiB Swap: 2048.0 total, 1024.3 free, 1023.7 used
세 숫자는 각각 1분, 5분, 15분 평균 대기 작업 수입니다. 코어 수와 비교해서 읽는 게 핵심입니다. 2코어 서버에서 load average가 2.0이면 딱 포화 상태고, 4.0이면 두 배로 밀려 있다는 뜻입니다. 코어 수는 nproc으로 확인합니다.
%Cpu(s) 줄의 wa는 디스크 I/O 대기 비율입니다. 이 값이 높으면 CPU가 아니라 디스크가 병목입니다. Swap의 used가 계속 올라가고 있다면 메모리 부족 신호입니다.
자주 쓰는 단축키
top은 화면이 떠 있는 상태에서 키를 누르면 바로 반응합니다.
| 키 | 동작 |
|---|---|
| P | CPU 사용률 순으로 정렬(기본값) |
| M | 메모리 사용률 순으로 정렬 |
| 1 | CPU 코어별로 나눠서 표시 |
| k | PID를 입력받아 종료 |
| c | 전체 명령줄 경로 보이기/숨기기 |
| q | 종료하고 빠져나오기 |
대문자 P와 M입니다. 소문자로 누르면 다른 동작이 걸립니다. k를 누르면 하단에 PID to signal/kill [기본값]: 이 뜨고, PID를 넣으면 시그널 번호를 다시 묻습니다. 그냥 엔터면 15(SIGTERM)로 나갑니다.
내 프로세스만 보고 싶으면 이렇게 띄웁니다.
top -u ubuntu
top vs htop
htop은 top을 보기 좋게 만든 도구입니다. 색이 들어가고, 마우스로 클릭할 수 있고, F9로 시그널을 골라 보낼 수 있습니다. 기본 설치는 아닌 경우가 많습니다.
sudo apt install htop # Debian, Ubuntu
sudo dnf install htop # RHEL, Rocky, AlmaLinux
서버에 함부로 패키지를 못 넣는 상황이면 top으로 충분합니다. 기능 차이는 편의성이지 정보량이 아닙니다.
kill로 종료하기: 시그널 순서를 지켜야 하는 이유
범인 PID를 찾았으면 이제 종료 단계입니다. 여기서 급하다고 바로 kill -9부터 치는 분들이 많은데, 순서가 있습니다.
kill은 이름과 달리 “죽이는” 명령이 아니라 시그널을 보내는 명령입니다. 어떤 시그널을 보내느냐에 따라 결과가 달라집니다.
SIGTERM과 SIGKILL 비교표
| 항목 | SIGTERM (15) | SIGKILL (9) |
|---|---|---|
| 명령 | kill PID 또는 kill -15 PID | kill -9 PID |
| 프로세스가 무시할 수 있나 | 있음 | 없음 |
| 정리 작업 | 저장·임시파일 정리 후 종료 | 즉시 강제 종료 |
| 위험도 | 낮음 | 데이터 손상 가능 |
| 언제 쓰나 | 항상 먼저 | TERM이 안 먹힐 때만 |
kill PID는 옵션 없이 쓰면 기본이 15번 SIGTERM입니다. 이건 “정리하고 나가라”는 요청입니다. 프로세스가 이 신호를 받으면 열어둔 파일을 닫고, 캐시를 디스크에 쓰고, 임시파일을 지운 다음 스스로 종료합니다.
kill 2841
몇 초 기다렸다가 확인합니다. 안 죽었으면 그때 9번을 씁니다.
kill -9 2841
SIGKILL은 커널이 프로세스를 강제로 끊어버리는 것이라 프로세스에게 정리할 기회 자체를 주지 않습니다. MySQL을 -9로 죽이면 테이블이 깨질 수 있고, 파일을 쓰던 중이면 반쯤 쓰인 파일이 남습니다. 그래서 순서가 중요합니다.

이름으로 종료할 때 주의점
PID를 매번 찾기 번거로우면 이름으로도 종료할 수 있습니다.
pkill -f "php-fpm: pool www"
killall nginx
pkill -f는 명령줄 전체에서 패턴을 찾아 매칭되는 걸 전부 종료합니다. killall은 프로세스 이름이 정확히 일치하는 걸 전부 종료합니다.
편한 만큼 위험합니다. pkill -f php라고 치면 php가 들어간 프로세스를 다 죽입니다. 관련 없는 배포 스크립트까지 같이 날아갈 수 있습니다. 실행 전에 어떤 게 잡히는지 먼저 확인하는 습관을 들이는 게 안전합니다.
pgrep -a -f "php-fpm: pool www"
이 목록이 예상과 같을 때만 pkill로 바꿔서 실행하면 됩니다.
정말 죽었는지 확인
종료 명령은 대부분 아무 출력 없이 끝납니다. 성공했는지 알려주지 않습니다. 확인은 직접 해야 합니다.
ps aux | grep 2841 | grep -v grep
아무것도 안 나오면 종료된 겁니다. 더 간단한 방법도 있습니다.
kill -0 2841
-0은 시그널을 실제로 보내지 않고 “보낼 수 있는지”만 확인하는 특수 값입니다. 프로세스가 살아 있으면 아무 출력 없이 끝나고, 없으면 이렇게 나옵니다.
bash: kill: (2841) - No such process
이 메시지가 뜨면 종료에 성공한 것입니다.
여기서 자주 막힙니다: 에러별 대처
명령대로 쳤는데 뜻대로 안 될 때가 있습니다. 갈림길을 미리 정리해 둡니다.
Operation not permitted
bash: kill: (1234) - Operation not permitted
가장 흔한 에러입니다. 해당 PID의 소유자가 내가 아니라는 뜻입니다. ps aux | grep 1234로 USER 열을 확인해 보면 root나 www-data로 되어 있을 겁니다.
sudo kill 1234
sudo를 붙여도 같은 메시지가 나온다면 컨테이너 안이거나, 커널 보호 대상 프로세스일 가능성이 있습니다.
No such process
kill: (1234): No such process
PID가 이미 없다는 뜻입니다. 이유는 둘 중 하나입니다. 이미 종료됐거나, PID를 잘못 봤거나. ps 출력에서 숫자를 옮겨 적다가 한 자리 틀리는 경우가 의외로 많습니다. 다시 ps aux | grep 프로세스명으로 현재 PID를 확인하세요. PID는 프로세스가 재시작되면 바뀝니다.
좀비 프로세스와 D 상태
kill -9를 해도 안 죽는 프로세스가 있습니다. STAT 열을 보면 답이 나옵니다.
Z(좀비)는 이미 죽은 프로세스입니다. 실행이 끝났는데 부모 프로세스가 종료 상태를 수거하지 않아서 목록에만 남아 있는 겁니다. 좀비 자체는 CPU도 메모리도 쓰지 않기 때문에 몇 개 있는 건 문제가 아닙니다. 정리하려면 좀비를 죽이는 게 아니라 부모를 처리해야 합니다.
ps -ef | grep defunct
ps -o ppid= -p 3021
두 번째 명령으로 부모 PID(PPID)를 확인한 다음, 그 부모를 재시작하면 좀비가 사라집니다. 좀비가 수백 개씩 쌓이면 그건 부모 프로그램의 버그입니다.
D(uninterruptible sleep)는 다릅니다. 디스크나 NFS 응답을 기다리며 커널 안에 들어가 있는 상태라, SIGKILL조차 전달되지 않습니다. 기다리는 I/O가 끝나야 풀립니다. NFS 마운트가 끊긴 서버에서 자주 봅니다. 이 경우 프로세스를 죽이는 게 아니라 마운트나 스토리지 쪽을 봐야 합니다.
죽여도 되살아날 때
죽였는데 몇 초 뒤에 ps를 다시 치면 PID만 바뀐 채 그대로 있는 경우가 있습니다. 이건 프로세스 관리자가 자동으로 재시작하고 있는 겁니다. systemd, supervisor, pm2 같은 것들이 그 역할을 합니다.
이럴 땐 kill이 아니라 관리자 쪽에 명령을 내려야 합니다.
systemctl status nginx
sudo systemctl stop nginx
systemctl status를 치면 해당 서비스가 관리 중인 메인 PID가 표시됩니다. 그 번호가 방금 죽였던 PID와 이어진다면 답이 나온 겁니다.
마지막으로, 절대 건드리면 안 되는 프로세스가 있습니다. PID 1은 init 또는 systemd입니다. 시스템 전체의 부모라서 죽이면 서버가 멈춥니다. sshd도 마찬가지입니다. 이걸 죽이면 지금 연결된 SSH 세션이 끊기고 다시 못 들어옵니다. 물리 콘솔이나 호스팅사 복구 모드가 없으면 손쓸 방법이 없습니다.
다음 단계: 재발을 막는 쪽으로
프로세스를 죽이는 건 응급처치입니다. 같은 일이 내일 또 생기면 의미가 없습니다. 매번 손으로 잡는 대신 원인이 남게 만드는 쪽으로 넘어갑니다.
먼저 종료하기 전에 상태를 저장해 두세요. 죽이고 나면 증거가 사라집니다.
ps aux --sort=-%mem > /tmp/ps_$(date +%Y%m%d_%H%M).txt
메모리가 원인이라면 커널의 OOM Killer가 개입했을 수 있습니다. 메모리가 바닥나면 커널이 임의로 프로세스를 골라 강제 종료하는 기능입니다. 흔적은 커널 로그에 남습니다.
dmesg | grep -i oom
sudo journalctl -k | grep -i "out of memory"
Out of memory: Killed process 1502 (mysqld) 같은 줄이 나오면, 그 프로세스가 죽은 이유는 버그가 아니라 메모리 부족입니다. 스왑을 늘리거나 워커 수를 줄이는 쪽으로 접근해야 합니다.
서비스 자체 로그도 같이 확인하세요.
sudo journalctl -u nginx --since "1 hour ago"
디스크가 원인인 경우도 배제하지 마세요. 로그 파일이 파티션을 꽉 채워서 서비스가 멈추는 일은 정말 자주 일어납니다. 디스크 용량 부족, df와 du로 범인 찾아내는 순서를 같이 확인해 보시면 원인 범위를 훨씬 빨리 좁힐 수 있습니다.
다음에 할 일 체크리스트
- [ ]
whoami,id로 지금 계정 권한 확인해 두기 - [ ]
ps aux --sort=-%mem | head -10을 셸 alias로 등록해 두기 - [ ]
top단축키 P, M, 1, k, q 다섯 개는 외워두기 - [ ] 종료할 땐
kill PID→ 확인 → 그래도 안 되면kill -9 PID순서 지키기 - [ ]
pkill -f쓰기 전에pgrep -a -f로 대상 먼저 확인하기 - [ ] 죽이기 전에
ps aux출력을 파일로 남겨두기 - [ ]
dmesg | grep -i oom으로 OOM Killer 흔적 있는지 확인하기 - [ ] 되살아나는 프로세스는
systemctl status 서비스명부터 확인하기 - [ ] PID 1과 sshd는 어떤 경우에도 건드리지 않기
