리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.

리눅스 시스템 기초 34 / 50 · Part 4. 프로세스·서비스·리소스
실습 환경: Rocky Linux 9 (10.0.0.200) · trap·STOP·종료 코드는 실습 환경 실제 출력
이전 글: 33. top으로 시스템 리소스 확인

1. 들어가며

29편에서 의심 프로세스를 발견했을 때 바로 kill하지 말고 kill -STOP으로 멈춘 뒤 증적을 수집하라 고 했다. 이번 글은 그 이유를 설명하기 위해 시그널(Signal) 을 정리한다.

kill이라는 이름 때문에 "프로세스를 죽이는 명령"으로 알기 쉽지만, 정확히는 프로세스에 시그널을 보내는 명령 이다. 어떤 시그널을 보내느냐에 따라 종료, 정지, 재개, 설정 재적재 등 동작이 완전히 달라진다.

이번 글에서 답할 질문:

  • 시그널은 무엇이고 어떻게 전달되는가?
  • SIGTERM, SIGKILL, SIGSTOP은 무엇이 다른가?
  • 왜 SIGKILL과 SIGSTOP은 프로세스가 막을 수 없는가?
  • 침해 대응에서 시그널을 어떤 순서로 써야 하는가?

2. 핵심 개념

2-1. 시그널이란

시그널은 커널이 프로세스에 보내는 비동기 알림 이다. 사용자(kill, Ctrl+C), 다른 프로세스, 커널 자신(메모리 오류, 자식 종료)이 보낼 수 있다.

2-2. 주요 시그널

번호이름기본 동작발생·용도
1SIGHUP종료터미널 연결 끊김. 데몬은 관례적으로 설정 다시 읽기 로 사용
2SIGINT종료Ctrl+C
3SIGQUIT종료 + 코어 덤프Ctrl+\
9SIGKILL종료강제 종료 — 가로채기·무시 불가
11SIGSEGV종료 + 코어 덤프잘못된 메모리 접근 (커널이 보냄)
15SIGTERM종료kill의 기본값. 정상 종료 요청
17SIGCHLD무시자식 프로세스 종료 알림 (31편)
18SIGCONT재개정지된 프로세스 재개
19SIGSTOP정지정지 — 가로채기·무시 불가
20SIGTSTP정지Ctrl+Z (가로채기 가능)

번호는 아키텍처에 따라 다를 수 있으므로(x86 Linux 기준), 스크립트에는 번호보다 이름(-TERM, -STOP) 을 쓰는 것이 안전하다. kill -l로 목록을 확인할 수 있다.

2-3. 시그널을 받은 프로세스의 선택지

선택의미예
기본 동작시그널마다 정해진 동작 (종료·정지 등)대부분의 프로그램
처리기(handler)프로그램이 정한 함수 실행웹 서버가 SIGTERM 받으면 연결 정리 후 종료
무시아무 일도 안 함nohup은 SIGHUP을 무시하게 함

SIGKILL과 SIGSTOP은 처리기 등록도, 무시도 할 수 없다. 커널이 직접 처리한다. 그래서 어떤 프로그램이든 이 두 시그널로는 반드시 종료·정지시킬 수 있다(단, D 상태는 I/O가 끝날 때까지 대기, 31편).


3. 동작 원리

시그널 전달 흐름과 침해 대응 시 kill 순서

  1. kill -TERM 1234를 실행하면 kill() System Call이 호출된다.
  2. 커널은 보낸 쪽의 권한 을 확인한다. 같은 사용자이거나 root(CAP_KILL)여야 한다. 일반 사용자는 다른 사람의 프로세스에 시그널을 보낼 수 없다.
  3. 커널이 대상 프로세스에 "대기 중인 시그널"을 표시한다.
  4. 대상 프로세스가 다음에 실행될 때 처리기·무시·기본 동작 중 하나가 일어난다.

실습 환경에서 시그널별 처리기를 등록한 스크립트에 시그널을 보낸 실제 결과다.

# trap.sh
trap 'echo "[trap] SIGTERM 받음 → 정리 후 종료"; exit 0' TERM
trap 'echo "[trap] SIGINT 무시"' INT
trap 'echo "[trap] SIGHUP 받음 → 설정 다시 읽기"' HUP
while true; do sleep 1; done
$ kill -HUP  <PID>   →  [trap] SIGHUP 받음 → 설정 다시 읽기
$ kill -INT  <PID>   →  [trap] SIGINT 무시
$ kill -STOP <PID>   →  ps STAT: T   (정지)
$ kill -CONT <PID>   →  ps STAT: S   (재개)
$ kill -TERM <PID>   →  [trap] SIGTERM 받음 → 정리 후 종료

$ sleep 60 & kill -9 $!; wait $!; echo $?
137          ← 128 + 9 : SIGKILL로 종료됨

종료 코드 128 + 시그널 번호 는 "그 시그널로 종료되었다"는 뜻이다. 137이면 SIGKILL, 143이면 SIGTERM이다. 서비스 로그에서 exit code 137이 보이면 누군가(또는 OOM Killer, 39편) 강제 종료했다는 뜻이다.


4. 실습

# 1) 시그널 목록
kill -l

# 2) 이름·패턴으로 보내기
pkill -TERM -u testuser            # 특정 사용자의 모든 프로세스
pkill -HUP rsyslogd                # 설정 재적재 (데몬마다 동작 확인 필요)
killall -STOP sysd                 # 이름이 정확히 일치하는 프로세스 전부

# 3) 정지와 재개
sleep 300 &
kill -STOP $!; ps -o pid,stat,comm -p $!
kill -CONT $!; ps -o pid,stat,comm -p $!
kill $!

# 4) 터미널이 끊겨도 계속 실행 — nohup (SIGHUP 무시)
nohup long_job.sh > job.log 2>&1 &
grep SigIgn /proc/$!/status        # 무시 중인 시그널 비트마스크

# 5) 프로세스가 무시·처리 중인 시그널 확인
grep -E '^Sig(Blk|Ign|Cgt)' /proc/<PID>/status

/proc/PID/status의 SigIgn(무시), SigCgt(처리기 등록) 값은 16진수 비트마스크다. 1번 비트(값 1)가 켜져 있으면 SIGHUP을 무시한다는 뜻이다.


5. 결과 분석

① 종료 코드로 원인 판단

종료 코드의미
0정상 종료
1~127프로그램이 정한 오류 코드
130SIGINT (Ctrl+C)
137SIGKILL — 강제 종료, OOM Killer 가능성
139SIGSEGV — 메모리 접근 오류(크래시)
143SIGTERM — 정상 종료 요청

② SigIgn 확인 결과 예 (형식 설명용)

SigBlk: 0000000000000000
SigIgn: 0000000000000001      ← SIGHUP 무시 (nohup으로 실행됨)
SigCgt: 0000000000014002

SIGHUP을 무시하는 프로세스는 사용자가 로그아웃해도 살아남도록 실행된 것이다. 정상 작업일 수도 있지만, 공격자가 세션 종료 후에도 도구를 계속 돌리려고 nohup을 쓰는 경우가 많다(27편 > /dev/null 2>&1 &와 함께).


6. 보안 관점

상황시그널 관점대응
공격 도구의 잔류nohup·setsid로 SIGHUP 무시, PPID 1SigIgn, PPID 확인 (31편)
보안 에이전트 종료공격자가 auditd·EDR·로그 수집기에 SIGKILL에이전트 종료 이벤트 경보, 서비스 자동 재시작
자기 보호 악성코드SIGTERM 처리기로 종료 거부SIGSTOP → 분석 → SIGKILL
증거 손실먼저 SIGKILL하면 /proc/PID, 메모리, 열린 연결 소멸SIGSTOP 먼저
다른 계정 프로세스일반 사용자는 불가, root만 가능계정 권한 분리 (20편)

특히 두 번째 항목이 중요하다. 공격자는 흔적을 줄이려고 로그·감사·보안 에이전트 프로세스를 먼저 종료 하려 한다. 그래서 auditd나 수집 에이전트가 예고 없이 멈추면, 그 자체를 높은 우선순위 경보 로 다뤄야 한다.


7. SOC / 보안관제 활용

7-1. 침해 대응 시 시그널 순서

P=7811
sudo kill -STOP $P                          # ① 동결 (행동 중지, 정보 유지)
sudo cp /proc/$P/exe /root/evidence/        # ② 증적 (29편)
sudo ss -tanp | grep "pid=$P," > /root/evidence/ss_$P.txt
sudo ls -l /proc/$P/fd > /root/evidence/fd_$P.txt
# ③ 차단: 통신 IP, 지속성(cron·systemd) 제거
sudo kill -TERM $P; sleep 3                 # ④ 정상 종료 요청
sudo kill -KILL $P 2>/dev/null              #    안 되면 강제 종료

7-2. 보안 에이전트 종료 감시

# auditd가 kill System Call을 기록하도록 (보안 프로세스 보호 목적)
-a always,exit -F arch=b64 -S kill -F a1=9 -k sigkill_sent

# 서비스 비정상 종료 기록
journalctl -u auditd --since today | grep -Ei 'killed|signal|exit'

7-3. 분석 흐름

[Alert]   SIEM: 서버 A 의 audit 로그 수집 중단 (02:33)
   ↓
[확인]    systemctl status auditd → "Main process exited, code=killed, status=9/KILL"
   ↓
[의미]    누군가 SIGKILL로 감사 데몬 종료 → 흔적 은폐 시도
   ↓
[교차]    02:33 직전 secure 로그: devuser 로그인, sudo 거부 → root 획득 경로 조사
   ↓
[Response] 서버 격리, 이미 SIEM에 전송된 02:33 이전 로그 기준으로 분석

8. 핵심 정리

  • kill은 프로세스에 시그널을 보내는 명령이다. 기본값은 SIGTERM(15, 정상 종료 요청).
  • 프로세스는 시그널을 기본 동작 / 처리기 / 무시 중 하나로 처리한다.
  • SIGKILL(9)과 SIGSTOP(19)은 막을 수 없다.
  • 종료 코드 128 + 번호: 137 = SIGKILL, 143 = SIGTERM, 139 = SIGSEGV.
  • nohup은 SIGHUP을 무시하게 해 로그아웃 후에도 살아남게 한다. SigIgn으로 확인한다.
  • 침해 대응: STOP → 증적 → 차단 → TERM → KILL. 먼저 KILL하면 증거가 사라진다.
  • 보안 에이전트의 예고 없는 종료는 높은 우선순위 경보다.

9. 다음 글

다음 글 「35. Parent / Child Process」 에서는 31편의 PPID와 이번 글의 SIGCHLD를 이어서, 프로세스 트리를 이용한 분석을 다룬다. sshd → bash → ls 같은 정상 트리와 httpd → sh → curl 같은 의심 트리를 구분하는 방법, 그리고 이중 fork로 부모를 끊는 기법을 정리한다.


참고 자료

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

0개의 댓글