서비스 · 프로세스 관리 12 / 50 · Part 2. 시그널·자원·세션
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정 analyst)

1. 들어가며

11편에서 시그널이 무엇인지 봤다. 이번에는 시그널을 보내는 도구 다. kill, pkill, killall은 모두 시그널을 보내지만 대상을 고르는 방식이 다르고, 그 차이 때문에 실무 사고가 난다. "java 프로세스 하나를 끄려다 서버의 모든 java를 껐다", "스크립트 이름으로 pkill 했더니 SSH 세션까지 끊겼다" 같은 일이다.

이번 글에서는 세 도구의 매칭 규칙 을 비교하고, pgrep으로 먼저 확인하는 안전한 작업 순서를 만든다.


2. 핵심 개념

2-1. 세 도구 비교

항목killpkillkillall
대상 지정PID, %작업번호패턴(정규식)프로세스 이름
기본 매칭 대상—comm (15자 이름)comm
매칭 방식정확부분 일치정확 일치
인자 전체 검색—-f— (일부 버전 -r 정규식)
사용자 지정—-u USER-u USER
패키지util-linux / 셸 빌트인procps-ngpsmisc
사전 확인 도구ps -ppgrep (같은 옵션)killall -i(대화형)

2-2. pgrep / pkill 주요 옵션

옵션의미
-aPID와 전체 명령 출력 (pgrep)
-lPID와 이름 출력
-f이름이 아닌 명령 인자 전체 에서 검색
-x정확히 일치
-u USER해당 사용자 프로세스만
-t pts/1해당 터미널
-n / -o가장 최근 / 가장 오래된 것 하나
-P PPID특정 부모의 자식
-e종료한 PID 출력 (pkill)
-c개수만

2-3. kill -0

kill -0 PID는 시그널을 보내지 않고 프로세스 존재 여부와 권한만 확인 한다. 종료 코드 0이면 존재하고 시그널을 보낼 권한이 있다는 뜻이다. 스크립트에서 PID 파일이 가리키는 프로세스가 살아 있는지 확인할 때 쓴다.


3. 동작 원리

kill · pkill · killall — 대상을 고르는 방식이 다르다

pkill -f 'sleep 1001'
  ↓ /proc/*/cmdline 을 모두 읽어 정규식 'sleep 1001' 검색
  ↓ 일치한 PID 목록 (자기 자신은 제외)
  ↓ 각 PID 에 kill(pid, SIGTERM)
  ↓ 권한 없는 PID → "Operation not permitted", 종료 코드 1

comm(이름)과 args(인자)는 다를 수 있다. exec -a 이름이나 프로그램 내부의 argv[0] 변경으로 인자 쪽 이름만 바꿀 수 있기 때문이다. 이름 기반 검색과 -f 검색의 결과가 다른 이유다.


4. 명령어 실습

# 1) 테스트 프로세스: 하나는 argv[0] 을 backup-job 으로 위장
sleep 1001 & sleep 1002 & (exec -a backup-job sleep 1003) &
pgrep -a sleep                      # 이름(comm) 기준
pgrep -a -f 'sleep 100[12]'         # 인자 기준 정규식
pgrep -l backup; pgrep -a -f backup # 이름 vs 인자
pkill -f 'sleep 1001'; pgrep -a sleep
kill -0 $(pgrep -f 'sleep 1002') && echo "1002 살아 있음"

# 2) (root) 사용자 지정과 매칭 방식 차이
su - analyst -c 'sleep 2001 & sleep 2002 &' ; sleep 2003 &
pkill -u analyst -x sleep           # analyst 의 sleep 만
killall -v sleep                    # 이름 정확 일치
sleep 2004 &
killall -v slee                     # 정확 일치 → 없음
pkill -e slee                       # 부분 일치 → sleep 종료!

# 3) (Ubuntu, analyst) 다른 사용자 프로세스
pkill -f 3001
pkill -x cron; echo "종료 코드=$?"

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — pgrep 으로 먼저 확인하고 pkill

실제 실행 결과 — Rocky Linux 9.8 · root@rocky9-lab — killall 과 pkill 의 매칭 차이

실제 실행 결과 — Ubuntu 24.04.5 · analyst@ubuntu-lab — pkill -f 의 함정: 자기 자신과 다른 사용자

텍스트 원본(실제 출력):

[analyst@rocky9-lab ~]$ sleep 1001 & sleep 1002 & (exec -a backup-job sleep 1003) &
[analyst@rocky9-lab ~]$ sleep 0.3; pgrep -a sleep
1156 sleep 1001
1157 sleep 1002
1158 backup-job 1003
[analyst@rocky9-lab ~]$ pgrep -a -f 'sleep 100[12]'
1156 sleep 1001
1157 sleep 1002
[analyst@rocky9-lab ~]$ pgrep -l backup; pgrep -a -f backup
1158 backup-job 1003
[analyst@rocky9-lab ~]$ pkill -f 'sleep 1001'; sleep 0.2; pgrep -a sleep
1157 sleep 1002
1158 backup-job 1003
[analyst@rocky9-lab ~]$ kill -0 $(pgrep -f 'sleep 1002') && echo "1002 살아 있음"
1002 살아 있음
[root@rocky9-lab ~]# su - analyst -c 'sleep 2001 & sleep 2002 &' ; sleep 2003 &
[root@rocky9-lab ~]# sleep 0.3; ps -o pid,user,cmd -C sleep
    PID USER     CMD
   1242 analyst  sleep 2001
   1243 analyst  sleep 2002
   1244 root     sleep 2003
[root@rocky9-lab ~]# pkill -u analyst -x sleep; sleep 0.2; ps -o pid,user,cmd -C sleep
    PID USER     CMD
   1244 root     sleep 2003
[root@rocky9-lab ~]# killall -v sleep; sleep 0.2
Killed sleep(1244) with signal 15
[root@rocky9-lab ~]# sleep 2004 & sleep 0.2
[root@rocky9-lab ~]# killall -v slee
slee: no process found
[root@rocky9-lab ~]# pkill -e slee
sleep killed (pid 1252)
analyst@ubuntu-lab:~$ sleep 3001 &
analyst@ubuntu-lab:~$ pgrep -a -f 3001
656 sleep 3001
analyst@ubuntu-lab:~$ pkill -f 3001 ; echo "pkill 종료 코드=$?"
pkill 종료 코드=0
analyst@ubuntu-lab:~$ pgrep -a -f 3001 || echo "3001 없음"
3001 없음
analyst@ubuntu-lab:~$ pkill -x cron; echo "종료 코드=$? (다른 사용자 프로세스: 권한 없음)"
pkill: killing pid 80 failed: Operation not permitted
종료 코드=1 (다른 사용자 프로세스: 권한 없음)

6. 결과 해석

관찰의미
pgrep -a sleep 결과에 1158 backup-job 1003 포함pgrep은 comm(sleep) 으로 찾았고, 출력(-a)은 args(backup-job 1003) 를 보여 줬다. 이름과 인자가 다른 프로세스다
pgrep -l backup → 결과 없음comm은 여전히 sleep이라 이름 검색에서는 찾을 수 없다
pgrep -a -f backup → 1158인자 검색(-f)에서만 찾았다
pkill -f 'sleep 1001' 후 1002·1003만 남음인자 정규식에 정확히 맞는 하나만 종료되었다
kill -0 → "1002 살아 있음"시그널 없이 존재·권한만 확인했다
pkill -u analyst -x sleep → root의 2003만 남음사용자 조건과 정확 일치를 함께 걸어 범위를 좁혔다
killall -v sleep → Killed sleep(1244) with signal 15killall 기본 시그널도 TERM(15)이다
killall -v slee → no process foundkillall은 이름이 정확히 같아야 한다
pkill -e slee → sleep killed (pid 1252)pkill은 부분 일치 라 slee로 sleep이 종료되었다. -x 없는 pkill의 위험이다
Ubuntu pkill -x cron → Operation not permitted, 종료 코드 1analyst는 root 소유 cron에 시그널을 보낼 수 없다

7. 보안 관점

주제내용
인자 위장악성 프로세스는 exec -a나 argv 덮어쓰기로 [kworker/0:1], sshd: root@pts/0 같은 이름을 흉내 낸다. comm과 args, /proc/PID/exe를 서로 비교 하면 불일치가 드러난다 (43편)
권한 경계일반 사용자는 다른 UID의 프로세스를 종료할 수 없다. 서비스 계정이 탈취되어도 root 서비스는 보호된다
오탐 조치관제 대응 중 pkill -f 패턴은 정상 프로세스까지 종료 할 수 있다. 서비스 중단도 보안 사고다
경쟁 조건PID를 확인한 뒤 kill하기까지 사이에 프로세스가 끝나고 PID가 재사용될 수 있다. 확인 즉시 조치하고, 필요하면 kill -STOP으로 먼저 멈춘다

8. 보안관제 관점

[조치 요청]  악성 프로세스 "kdevtmpfsi" 종료 요청
     ↓
[① 목록 확인]  pgrep -a -x kdevtmpfsi
               pgrep -a -f kdevtmpfsi          ← 인자에만 있는 것도 확인
     ↓
[② 증거 수집]  PID 별 /proc/PID/exe, cmdline, 네트워크 (05·16편)
     ↓
[③ 정지]       kill -STOP <PID들>              ← 재생성·통신 차단, 증거 보존
     ↓
[④ 부모 확인]  ps -o ppid= -p <PID> → 부모(워치독)부터 처리
     ↓
[⑤ 종료]       kill -KILL <PID들>              ← 악성은 TERM 핸들러로 버틸 수 있다
     ↓
[⑥ 확인]       pgrep -a -f kdevtmpfsi || echo 제거 완료
원칙이유
확인 명령과 조치 명령의 조건을 똑같이pgrep 결과가 곧 pkill 대상이다
가능하면 PID 직접 지정범위가 명확하고 보고서에 기록하기 쉽다
조치 명령을 그대로 기록무엇을 종료했는지 재현·설명할 수 있어야 한다

9. 실무에서 자주 발생하는 실수

실수결과예방
pkill java서버의 모든 java 서비스 종료pgrep -a java로 확인, PID 지정
pkill -f backup.sh를 SSH 세션에서 실행인자에 backup.sh가 들어간 내 셸·sudo·편집기 까지 매칭될 수 있다-x, -u, -t로 범위 제한
killall을 다른 유닉스와 같다고 착각Solaris 등의 killall은 모든 프로세스를 종료 한다Linux 외 시스템에서는 사용하지 않는다
이름이 15자를 넘는 프로세스를 -x로 검색comm은 15자로 잘려 불일치pgrep -f 또는 잘린 이름 사용
종료 코드 확인 없이 다음 단계 진행권한 오류로 실제로는 안 죽었을 수 있다$?와 재확인

10. 실습 체크리스트

[ ] pgrep 의 이름 검색과 -f 인자 검색 결과 차이를 확인했다
[ ] exec -a 로 바뀐 args 와 comm 이 다른 것을 확인했다
[ ] kill -0 으로 존재 여부를 확인했다
[ ] pkill -u, -x 로 대상을 좁혔다
[ ] killall 정확 일치와 pkill 부분 일치 차이를 확인했다
[ ] 다른 사용자 프로세스에 대한 Operation not permitted 를 확인했다

11. 핵심 정리

  • kill은 PID, pkill은 패턴(부분 일치·정규식), killall은 이름(정확 일치)으로 대상을 고른다.
  • 기본 검색 대상은 comm(이름)이고, -f는 인자 전체다. 둘은 다를 수 있다.
  • pkill에는 확인 단계가 없다. 같은 조건의 pgrep -a로 먼저 확인 한다.
  • 일반 사용자는 자기 UID 프로세스에만 시그널을 보낼 수 있다.
  • 관제 조치 순서: 목록 확인 → 증거 수집 → STOP → 부모 처리 → KILL → 재확인.

12. 다음 편 예고

다음 글 「13. SIGTERM과 SIGKILL — 안전한 종료」 에서는 왜 kill -9를 먼저 쓰면 안 되는지를 락 파일이 남는 실험 으로 확인하고, systemctl stop이 내부적으로 TERM → 타임아웃 → KILL 순서로 동작하는 과정을 로그로 추적한다.


참고 자료


시리즈 이동

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글