리눅스 명령어

ps와 top으로 프로세스 확인하고 안전하게 종료하는 순서

ps와 top으로 프로세스 확인하고 안전하게 종료하는 순서

사이트 접속이 갑자기 느려지고, 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에 넣을 숫자
%CPUCPU 점유율100 넘으면 멀티코어 합산값
%MEM물리 메모리 점유율전체 램 대비 비율
RSS실제 사용 중인 메모리(KB)%MEM보다 정확한 절대값
STAT프로세스 상태Z나 D면 문제 신호
COMMAND실행된 명령 전체어떤 프로그램인지 식별

STAT 열은 나중에 중요해지니 여기서 정리하고 갑니다.

STAT상태설명
RRunning실행 중이거나 실행 대기
SSleeping뭔가를 기다리는 중, 대부분 정상
DUninterruptible sleep디스크·NFS I/O 대기, kill이 안 먹힘
ZZombie이미 죽었는데 부모가 정리 안 함
TStopped정지됨(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은 화면이 떠 있는 상태에서 키를 누르면 바로 반응합니다.

키동작
PCPU 사용률 순으로 정렬(기본값)
M메모리 사용률 순으로 정렬
1CPU 코어별로 나눠서 표시
kPID를 입력받아 종료
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 PIDkill -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는 어떤 경우에도 건드리지 않기

댓글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다