
서비스 · 프로세스 관리 06 / 50 · Part 1. 프로세스 기초
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
ps는 가장 많이 쓰는 프로세스 명령어지만, 옵션 문법이 세 가지(BSD·UNIX·GNU)나 섞여 있어 "외워서 쓰는" 경우가 많다. ps aux와 ps -ef는 무엇이 다른지, 긴 명령어가 왜 잘리는지, 원하는 열만 뽑아 정렬하려면 어떻게 하는지를 정리해 두면 관제·점검 스크립트를 훨씬 정확하게 만들 수 있다.
이번 글에서는 ps 옵션을 선택(어떤 프로세스) → 출력(어떤 열) → 정렬 의 구조로 나누어 정리하고, 관제에서 바로 쓰는 템플릿을 만든다.
「리눅스 시스템 기초 32편」에서 ps의 기본 사용법을 다뤘다. 이번 글은 옵션 체계와
-o활용에 집중한다.
| 스타일 | 형태 | 대표 예 | 특징 |
|---|---|---|---|
| BSD | 대시 없음 | ps aux | %CPU·%MEM·RSS 중심, 자원 확인에 편하다 |
| UNIX(System V) | 대시 하나 | ps -ef | PPID가 보여 부모 추적에 편하다 |
| GNU long | 대시 둘 | --sort, --forest, --ppid | 정렬·트리·세부 선택 |
ps aux의 a, u, x와 ps -aux는 다른 뜻 이다. 대시를 붙이면 UNIX 문법으로 해석되어 "사용자 x의 프로세스"를 찾으려 하기 때문에 경고가 날 수 있다. BSD 옵션에는 대시를 붙이지 않는다.
| 옵션 | 의미 |
|---|---|
-e / ax | 모든 프로세스 |
-p 1,31 | 특정 PID |
-u analyst | 특정 사용자(effective UID) |
-C sshd,crond | 실행 파일 이름(comm)으로 선택 |
--ppid 356 | 특정 부모의 자식 |
-t pts/0 | 특정 터미널 |
-o)| 열 | 의미 | 왜 필요한가 |
|---|---|---|
pid,ppid | 자신과 부모 | 실행 주체 추적 |
user / uid | 실행 계정 | 권한 판단 |
lstart | 정확한 시작 시각 | 로그와 시간 상관 분석 |
etimes | 실행 후 경과 초 | "최근 1시간 안에 시작된 프로세스" 필터 |
stat | 상태 | 04편 |
%cpu,%mem,rss | 자원 사용량 | 마이너·메모리 누수 |
comm | 짧은 이름(15자) | 이름 기반 선택 |
args / cmd | 전체 명령 인자 | 실행 내용 확인 |
nlwp | 스레드 수 | 멀티스레드 여부 |

ps는 /proc/[0-9]*를 모두 읽고(05편) 선택 조건으로 거른 뒤, 요청한 열만 표로 만든다.
/proc/1, /proc/21, /proc/31 ... (모든 PID 디렉터리)
↓ 선택: -e / -u / -p / -C / --ppid
↓ 출력: -o 열 목록 → /proc/PID/stat, status, cmdline 에서 값 추출
↓ 정렬: --sort=-rss
화면 출력 (터미널 폭에 맞춰 마지막 열을 자름)
마지막 줄이 중요하다. ps는 터미널 폭에 맞춰 명령어 열을 자른다. 파이프로 다른 명령에 넘길 때도 폭 제한이 걸릴 수 있으므로, 전체 인자가 필요하면 -ww를 붙인다.
# 1) BSD vs UNIX 스타일
ps aux | head -6
ps -ef | head -6
# 2) 필요한 열만 + 정렬 (메모리 많이 쓰는 순)
ps -eo pid,ppid,user,%cpu,%mem,rss,etimes,stat,comm --sort=-rss | head -8
# 3) 정확한 시작 시각과 경과 시간
ps -o pid,user,lstart,etime,cmd -p 1,$(pgrep -xo sshd)
# 4) 이름·사용자로 선택
ps -C sshd,sshd-session -o pid,ppid,user,args
ps -u analyst -o pid,tty,stat,cmd
# 5) 사용자별 프로세스 수 집계
ps axo user= | sort | uniq -c | sort -rn
# 6) (Ubuntu) 긴 인자와 스레드
ps -o pid,nlwp,stat,cmd -p $P # 잘린 출력
ps -ww -o pid,cmd -p $P # 전체 출력
ps -L -o pid,lwp,nlwp,stat,comm -p $P # 스레드별



텍스트 원본(실제 출력):
[analyst@rocky9-lab ~]$ ps aux | head -6
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.3 0.1 24388 13964 ? Ss 11:48 0:00 /usr/sbin/init
root 21 0.0 0.1 28512 11128 ? Ss 11:48 0:00 /usr/lib/systemd/systemd-journald
root 31 0.0 0.0 11788 7940 ? Ss 11:48 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-
root 32 0.0 0.1 20904 9728 ? Ss 11:48 0:00 /usr/lib/systemd/systemd-logind
root 37 0.0 0.0 4728 2708 ? Ss 11:48 0:00 /usr/sbin/atd -f
[analyst@rocky9-lab ~]$ ps -ef | head -6
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 11:48 ? 00:00:00 /usr/sbin/init
root 21 1 0 11:48 ? 00:00:00 /usr/lib/systemd/systemd-journald
root 31 1 0 11:48 ? 00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 32 1 0 11:48 ? 00:00:00 /usr/lib/systemd/systemd-logind
root 37 1 0 11:48 ? 00:00:00 /usr/sbin/atd -f
[analyst@rocky9-lab ~]$ ps aux | wc -l; ps -ef | wc -l
17
17
[root@rocky9-lab ~]# ps -eo pid,ppid,user,%cpu,%mem,rss,etimes,stat,comm --sort=-rss | head -8
PID PPID USER %CPU %MEM RSS ELAPSED STAT COMMAND
1 0 root 0.3 0.1 13964 73 Ss systemd
437 1 root 0.0 0.1 11964 0 Ss systemd
385 1 analyst 2.0 0.1 11932 1 Ss systemd
433 31 root 1.0 0.1 11528 1 Ss sshd-session
21 1 root 0.0 0.1 11156 67 Ss systemd-journal
32 1 root 0.0 0.1 9728 65 Ss systemd-logind
31 1 root 0.0 0.0 7940 65 Ss sshd
[root@rocky9-lab ~]# ps -o pid,user,lstart,etime,cmd -p 1,$(pgrep -xo sshd)
PID USER STARTED ELAPSED CMD
1 root Thu Sep 24 11:48:18 2026 01:13 /usr/sbin/init
31 root Thu Sep 24 11:48:26 2026 01:05 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
[root@rocky9-lab ~]# ps -C sshd,sshd-session -o pid,ppid,user,args
PID PPID USER COMMAND
31 1 root sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
433 31 root sshd-session: root [priv]
446 433 root sshd-session: root@pts/0
[root@rocky9-lab ~]# ps -u analyst -o pid,tty,stat,cmd
PID TT STAT CMD
385 ? Ss /usr/lib/systemd/systemd --user
387 ? S (sd-pam)
[root@rocky9-lab ~]# ps axo user= | sort | uniq -c | sort -rn
16 root
2 dbus
2 analyst
root@ubuntu-lab:~# python3 -c 'import threading,time; [threading.Thread(target=time.sleep,args=(60,),daemon=True).start() for _ in range(3)]; time.sleep(60)' --very-long-argument-for-width-test=/opt/app/config/production/settings.yaml &
root@ubuntu-lab:~# sleep 1.5; P=$!
root@ubuntu-lab:~# ps -o pid,nlwp,stat,cmd -p $P
PID NLWP STAT CMD
378 4 Sl+ python3 -c import threading,time; [threading.Thread(target=time.sleep,args=(60,),daemon=True
root@ubuntu-lab:~# ps -ww -o pid,cmd -p $P
PID CMD
378 python3 -c import threading,time; [threading.Thread(target=time.sleep,args=(60,),daemon=True).start() for _ in range(3)]; time.sleep(60) --very-long-argument-for-width-test=/opt/app/config/production/settings.yaml
root@ubuntu-lab:~# ps -L -o pid,lwp,nlwp,stat,comm -p $P
PID LWP NLWP STAT COMMAND
378 378 4 Sl+ python3
378 380 4 Sl+ python3
378 381 4 Sl+ python3
378 382 4 Sl+ python3
root@ubuntu-lab:~# ps -eo pid,stat,cmd --no-headers | awk '$2 ~ /l/' | head -3
378 Sl+ python3 -c import threading,time; [threading.Thread(target=time.sleep,args=(60,),daemon=True).sta
root@ubuntu-lab:~# kill $P
| 관찰 | 의미 |
|---|---|
ps aux 헤더: %CPU %MEM VSZ RSS ... START | 자원 중심 형식. VSZ는 가상 메모리, RSS는 실제 물리 메모리(KB) |
ps -ef 헤더: UID PID PPID C STIME | 부모 PID가 바로 보인다. 트리 분석은 -ef가 편하다 |
| 두 결과 모두 18줄 | 형식만 다르고 대상은 같다 (헤더 포함 17개 프로세스) |
--sort=-rss 결과 1위가 systemd | -는 내림차순. 메모리 순위를 바로 확인할 수 있다 |
ELAPSED 0 인 sshd-session·systemd | 지금 막 로그인해서 생긴 프로세스다. etimes로 "최근에 생긴 것"을 골라낼 수 있다 |
lstart = Thu Sep 24 11:48:26 2026 | 초 단위 정확한 시각. START(시:분) 열보다 로그 대조에 적합하다 |
ps -C sshd,sshd-session | 콤마로 여러 이름을 한 번에 선택했다. listener → [priv] → root@pts/0 구조가 보인다 |
uniq -c 결과 root 16, dbus 2, analyst 2 | 계정별 프로세스 수. 평소 기준선과 비교하면 이상 계정 활동을 찾을 수 있다 |
| Ubuntu 기본 출력에서 인자 뒤가 잘림 | 터미널 폭(110열) 때문. -ww에서 --very-long-argument...settings.yaml까지 전부 보였다 |
NLWP 4, ps -L에서 LWP 4개 | 메인 스레드 1 + 생성한 스레드 3. 모두 같은 PID(378)를 공유한다 |
STAT Sl+ | l = 멀티스레드. 04편의 보조 플래그 |
| 주제 | 내용 |
|---|---|
| 잘린 명령어 | 공격 명령은 길다(base64 페이로드, 긴 URL). -ww 없이 보면 핵심 부분이 잘려 판단을 놓친다 |
| args vs comm | args는 프로세스가 스스로 바꿀 수 있다(exec -a, prctl). comm도 바꿀 수 있다. 최종 판단은 /proc/PID/exe로 한다 (43편) |
| 계정별 집계 | 평소 프로세스가 없던 서비스 계정(www-data, nginx)의 프로세스 수가 늘면 조사 대상이다 |
| 시간 기준 | etimes로 "최근 N초 안에 시작된 프로세스"를 뽑으면 침해 직후 생긴 프로세스를 빠르게 좁힐 수 있다 |
[Detection] 침해 의심 서버 — "지금 무엇이 돌고 있나?"를 한 번에 저장
↓
[수집] ps -eo pid,ppid,user,lstart,etimes,stat,%cpu,%mem,args -ww --sort=-%cpu > ps_$(date +%F_%H%M).txt
↓
[좁히기] ps -eo pid,etimes,user,args -ww | awk '$2 < 3600' # 최근 1시간
ps -eo user= | sort | uniq -c | sort -rn # 계정별 수
ps -eo pid,user,%cpu,args --sort=-%cpu | head # CPU 상위
↓
[판단] 기준선(baseline)과 비교 → 새 계정·새 프로세스·비정상 경로
↓
[Response] 의심 PID 로 /proc 증거 수집(05편) → 조치
| 관점 | 내용 |
|---|---|
| 증거 보존 | ps 결과는 시각이 들어간 파일로 저장한다. 조치 전후를 비교할 수 있다 |
| baseline | 정상 상태의 ps -eo user,comm 목록을 평소에 저장해 두면 차이 비교가 쉽다 |
| 스크립트 | 사람이 읽을 때는 aux, 스크립트는 -eo ... --no-headers로 파싱하기 쉬운 형식을 쓴다 |
| 실수 | 결과 | 예방 |
|---|---|---|
ps -aux (대시 + BSD 옵션) | 경고 또는 의도와 다른 선택 | BSD 옵션은 대시 없이 ps aux |
ps aux \| grep nginx | grep 자신이 결과에 섞인다 | pgrep -a nginx 또는 ps -C nginx |
| 명령어가 잘린 결과로 판단 | 악성 인자를 놓침 | -ww 또는 /proc/PID/cmdline |
START 열로 시간 대조 | 하루 지난 프로세스는 날짜만 보인다 | lstart 사용 |
%CPU를 순간값으로 이해 | ps의 %CPU는 수명 전체 평균 이다 | 순간 사용률은 top(07편) |
[ ] ps aux 와 ps -ef 의 열 차이를 설명할 수 있다
[ ] -o 로 pid,ppid,user,lstart,etimes,args 를 출력했다
[ ] --sort=-rss 로 메모리 상위 프로세스를 정렬했다
[ ] -C, -u, -p, --ppid 로 대상을 선택했다
[ ] -ww 로 잘리지 않은 전체 인자를 확인했다
[ ] -L / nlwp 로 스레드를 확인했다
aux)·UNIX(-ef)·GNU(--sort) 문법을 모두 받는다. BSD 옵션에는 대시를 붙이지 않는다.lstart(정확한 시각), etimes(경과 초), args -ww(전체 인자)가 특히 중요하다.%CPU는 평균값이다. 순간 부하는 top으로 본다.다음 글 「07. top으로 실시간 자원 모니터링」 에서는 top의 요약 영역(load average, %Cpu, Mem)을 해석하고, 배치 모드(-b) 로 결과를 파일에 남기는 방법, 정렬·사용자 필터·스레드 보기를 실습한다.