리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 34 / 50 · Part 4. 프로세스·서비스·리소스
실습 환경: Rocky Linux 9 (10.0.0.200) · trap·STOP·종료 코드는 실습 환경 실제 출력
이전 글: 33. top으로 시스템 리소스 확인
29편에서 의심 프로세스를 발견했을 때 바로 kill하지 말고 kill -STOP으로 멈춘 뒤 증적을 수집하라 고 했다. 이번 글은 그 이유를 설명하기 위해 시그널(Signal) 을 정리한다.
kill이라는 이름 때문에 "프로세스를 죽이는 명령"으로 알기 쉽지만, 정확히는 프로세스에 시그널을 보내는 명령 이다. 어떤 시그널을 보내느냐에 따라 종료, 정지, 재개, 설정 재적재 등 동작이 완전히 달라진다.
이번 글에서 답할 질문:
시그널은 커널이 프로세스에 보내는 비동기 알림 이다. 사용자(kill, Ctrl+C), 다른 프로세스, 커널 자신(메모리 오류, 자식 종료)이 보낼 수 있다.
| 번호 | 이름 | 기본 동작 | 발생·용도 |
|---|---|---|---|
| 1 | SIGHUP | 종료 | 터미널 연결 끊김. 데몬은 관례적으로 설정 다시 읽기 로 사용 |
| 2 | SIGINT | 종료 | Ctrl+C |
| 3 | SIGQUIT | 종료 + 코어 덤프 | Ctrl+\ |
| 9 | SIGKILL | 종료 | 강제 종료 — 가로채기·무시 불가 |
| 11 | SIGSEGV | 종료 + 코어 덤프 | 잘못된 메모리 접근 (커널이 보냄) |
| 15 | SIGTERM | 종료 | kill의 기본값. 정상 종료 요청 |
| 17 | SIGCHLD | 무시 | 자식 프로세스 종료 알림 (31편) |
| 18 | SIGCONT | 재개 | 정지된 프로세스 재개 |
| 19 | SIGSTOP | 정지 | 정지 — 가로채기·무시 불가 |
| 20 | SIGTSTP | 정지 | Ctrl+Z (가로채기 가능) |
번호는 아키텍처에 따라 다를 수 있으므로(x86 Linux 기준), 스크립트에는 번호보다 이름(
-TERM,-STOP) 을 쓰는 것이 안전하다.kill -l로 목록을 확인할 수 있다.
| 선택 | 의미 | 예 |
|---|---|---|
| 기본 동작 | 시그널마다 정해진 동작 (종료·정지 등) | 대부분의 프로그램 |
| 처리기(handler) | 프로그램이 정한 함수 실행 | 웹 서버가 SIGTERM 받으면 연결 정리 후 종료 |
| 무시 | 아무 일도 안 함 | nohup은 SIGHUP을 무시하게 함 |
SIGKILL과 SIGSTOP은 처리기 등록도, 무시도 할 수 없다. 커널이 직접 처리한다. 그래서 어떤 프로그램이든 이 두 시그널로는 반드시 종료·정지시킬 수 있다(단, D 상태는 I/O가 끝날 때까지 대기, 31편).

kill -TERM 1234를 실행하면 kill() System Call이 호출된다.CAP_KILL)여야 한다. 일반 사용자는 다른 사람의 프로세스에 시그널을 보낼 수 없다.실습 환경에서 시그널별 처리기를 등록한 스크립트에 시그널을 보낸 실제 결과다.
# 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편) 강제 종료했다는 뜻이다.
# 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을 무시한다는 뜻이다.
① 종료 코드로 원인 판단
| 종료 코드 | 의미 |
|---|---|
| 0 | 정상 종료 |
| 1~127 | 프로그램이 정한 오류 코드 |
| 130 | SIGINT (Ctrl+C) |
| 137 | SIGKILL — 강제 종료, OOM Killer 가능성 |
| 139 | SIGSEGV — 메모리 접근 오류(크래시) |
| 143 | SIGTERM — 정상 종료 요청 |
② SigIgn 확인 결과 예 (형식 설명용)
SigBlk: 0000000000000000
SigIgn: 0000000000000001 ← SIGHUP 무시 (nohup으로 실행됨)
SigCgt: 0000000000014002
SIGHUP을 무시하는 프로세스는 사용자가 로그아웃해도 살아남도록 실행된 것이다. 정상 작업일 수도 있지만, 공격자가 세션 종료 후에도 도구를 계속 돌리려고 nohup을 쓰는 경우가 많다(27편 > /dev/null 2>&1 &와 함께).
| 상황 | 시그널 관점 | 대응 |
|---|---|---|
| 공격 도구의 잔류 | nohup·setsid로 SIGHUP 무시, PPID 1 | SigIgn, PPID 확인 (31편) |
| 보안 에이전트 종료 | 공격자가 auditd·EDR·로그 수집기에 SIGKILL | 에이전트 종료 이벤트 경보, 서비스 자동 재시작 |
| 자기 보호 악성코드 | SIGTERM 처리기로 종료 거부 | SIGSTOP → 분석 → SIGKILL |
| 증거 손실 | 먼저 SIGKILL하면 /proc/PID, 메모리, 열린 연결 소멸 | SIGSTOP 먼저 |
| 다른 계정 프로세스 | 일반 사용자는 불가, root만 가능 | 계정 권한 분리 (20편) |
특히 두 번째 항목이 중요하다. 공격자는 흔적을 줄이려고 로그·감사·보안 에이전트 프로세스를 먼저 종료 하려 한다. 그래서 auditd나 수집 에이전트가 예고 없이 멈추면, 그 자체를 높은 우선순위 경보 로 다뤄야 한다.
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 # 안 되면 강제 종료
# 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'
[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 이전 로그 기준으로 분석
kill은 프로세스에 시그널을 보내는 명령이다. 기본값은 SIGTERM(15, 정상 종료 요청).nohup은 SIGHUP을 무시하게 해 로그아웃 후에도 살아남게 한다. SigIgn으로 확인한다.다음 글 「35. Parent / Child Process」 에서는 31편의 PPID와 이번 글의 SIGCHLD를 이어서, 프로세스 트리를 이용한 분석을 다룬다. sshd → bash → ls 같은 정상 트리와 httpd → sh → curl 같은 의심 트리를 구분하는 방법, 그리고 이중 fork로 부모를 끊는 기법을 정리한다.