
리눅스 시스템 기초 · 03. 서비스 · 프로세스 관리 — 이 영역 글의 "N편"은 제목 앞 번호(영역 내 번호)다. 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
서비스 · 프로세스 관리 01 / 50 · Part 1. 프로세스 기초
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
「서비스 · 프로세스 관리」 시리즈의 첫 글이다. 이 시리즈는 프로세스가 만들어지고 실행되고 종료되는 원리에서 출발해 시그널, 자원 제한, systemd 서비스, cron·timer 스케줄링을 차례로 다루고, 마지막 Part 5에서는 보안관제(SOC) 관점에서 의심 프로세스와 지속성(persistence) 흔적을 탐지 하는 실습으로 마무리한다.
첫 주제는 가장 기초적이지만 가장 자주 헷갈리는 구분인 프로그램과 프로세스의 차이 다. 관제 화면에서 "의심 프로세스"를 보고했을 때 "그 파일은 지금 어디에 있나요?", "파일을 지우면 끝나나요?"라는 질문에 정확히 답하려면 이 구분이 먼저 서 있어야 한다.
「리눅스 시스템 기초 31편」이 프로세스를 개괄했다면, 이 시리즈는 생성(02편), 트리(03편), 상태(04편), /proc(05편)으로 나누어 각각 깊게 다룬다.
| 구분 | 프로그램 (Program) | 프로세스 (Process) |
|---|---|---|
| 정체 | 디스크에 저장된 실행 파일 | 메모리에 올라가 실행 중인 프로그램의 인스턴스 |
| 상태 | 정적 (움직이지 않음) | 동적 (생성 → 실행 → 대기 → 종료) |
| 식별자 | 경로, inode | PID |
| 수명 | 삭제하기 전까지 유지 | 종료되면 사라짐 (재부팅 시 모두 사라짐) |
| 개수 | 하나 | 같은 프로그램으로 여러 개 생성 가능 |
| 확인 방법 | ls -l, file, stat | ps, pgrep, /proc/PID |
비유하면 프로그램은 요리 레시피 이고, 프로세스는 그 레시피로 지금 요리하고 있는 주방 이다. 레시피 한 장으로 주방 여러 곳이 동시에 요리할 수 있고, 각 주방은 자기 재료(메모리)와 도구(파일 디스크립터)를 따로 가진다.
커널은 프로세스마다 task_struct라는 구조체로 다음 정보를 관리한다.
| 항목 | 설명 | 어디서 보나 |
|---|---|---|
| PID / PPID | 자신과 부모의 번호 | ps -o pid,ppid |
| 자격 증명 | 실행 계정 UID·GID (real/effective) | /proc/PID/status의 Uid: |
| 메모리 공간 | 코드, 데이터, 힙, 스택, 공유 라이브러리 | /proc/PID/maps |
| 실행 파일 | 어떤 프로그램에서 왔는가 | /proc/PID/exe |
| 열린 파일 | 파일·소켓·파이프 (파일 디스크립터) | /proc/PID/fd/ |
| 작업 디렉터리 | 상대경로의 기준 | /proc/PID/cwd |
| 환경변수·인자 | 실행 시 전달된 값 | /proc/PID/environ, cmdline |
| 상태 | R, S, D, T, Z | ps -o stat |
스레드는 한 프로세스 안에서 메모리를 공유하는 실행 흐름 이다. Linux 커널은 스레드도 task로 관리하므로 각 스레드에 TID가 붙지만, 같은 프로세스의 스레드는 PID(TGID)를 공유한다. ps -eLf나 /proc/PID/task/에서 확인할 수 있다. 이 시리즈에서 "프로세스"는 특별한 언급이 없으면 스레드 그룹 전체를 뜻한다.

프로그램이 프로세스가 되는 흐름은 다음과 같다.
[디스크] /usr/bin/sleep (ELF 실행 파일)
↓ 셸이 fork()로 자기 복제본(자식)을 만든다
[자식 프로세스] 아직 bash 코드를 가진 상태
↓ execve("/usr/bin/sleep") — 메모리를 sleep 코드로 교체
[프로세스 PID 194] 커널이 PID·메모리·fd·자격 증명을 관리
↓ 실행이 끝나면 exit() → 부모가 wait()로 회수
[종료] PID 반환, 메모리 해제 — 디스크의 프로그램은 그대로
핵심은 두 가지다.
/proc/PID/maps에 /usr/bin/sleep이 여러 줄 나타나는 이유다.rm) inode는 바로 해제되지 않고 프로세스는 계속 돈다. fork·exec 과정은 02편에서 strace로 직접 확인한다.# 1) 프로그램 (디스크의 파일) 확인
ls -l /usr/bin/sleep
file /usr/bin/sleep
# 2) 같은 프로그램으로 프로세스 두 개 실행
sleep 300 & sleep 300 &
pgrep -a sleep
for p in $(pgrep -x sleep); do echo "PID=$p exe=$(readlink /proc/$p/exe) ..."; done
ps -o pid,ppid,stat,lstart,cmd -C sleep
# 3) 프로세스를 종료해도 프로그램은 남는다
kill %1 %2
pgrep -a sleep || echo "sleep 프로세스 없음"; ls -l /usr/bin/sleep
# 4) (Ubuntu) 실행 파일을 지워도 프로세스는 남는다
cp /usr/bin/sleep ~/mysleep && ~/mysleep 300 &
ls -l /proc/$P/exe
rm ~/mysleep
ls -l /proc/$P/exe # → '(deleted)' 표시
4)의 삭제 실습은 반드시 자기 홈 디렉터리의 복사본 으로 한다. 시스템 바이너리(
/usr/bin/*)를 지우는 실습은 하지 않는다.


텍스트 원본(실제 출력):
[analyst@rocky9-lab ~]$ ls -l /usr/bin/sleep
-rwxr-xr-x 1 root root 32224 Sep 10 16:13 /usr/bin/sleep
[analyst@rocky9-lab ~]$ file /usr/bin/sleep
/usr/bin/sleep: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=933051efe29ce3924d09ee266336e9b2366ddd7f, for GNU/Linux 3.2.0, stripped
[analyst@rocky9-lab ~]$ sleep 300 & sleep 300 &
[analyst@rocky9-lab ~]$ pgrep -a sleep
194 sleep 300
195 sleep 300
[analyst@rocky9-lab ~]$ for p in $(pgrep -x sleep); do echo "PID=$p exe=$(readlink /proc/$p/exe) ppid=$(awk '/^PPid/{print $2}' /proc/$p/status) vmrss=$(awk '/^VmRSS/{print $2$3}' /proc/$p/status)"; done
PID=194 exe=/usr/bin/sleep ppid=169 vmrss=1944kB
PID=195 exe=/usr/bin/sleep ppid=169 vmrss=1988kB
[analyst@rocky9-lab ~]$ ps -o pid,ppid,stat,lstart,cmd -C sleep
PID PPID STAT STARTED CMD
194 169 S+ Thu Sep 24 09:38:17 2026 sleep 300
195 169 S+ Thu Sep 24 09:38:17 2026 sleep 300
[analyst@rocky9-lab ~]$ kill %1 %2; wait 2>/dev/null
[analyst@rocky9-lab ~]$ pgrep -a sleep || echo "sleep 프로세스 없음 (프로그램 파일은 그대로)"; ls -l /usr/bin/sleep
sleep 프로세스 없음 (프로그램 파일은 그대로)
-rwxr-xr-x 1 root root 32224 Sep 10 16:13 /usr/bin/sleep
analyst@ubuntu-lab:~$ cp /usr/bin/sleep ~/mysleep && ~/mysleep 300 &
analyst@ubuntu-lab:~$ sleep 0.3; P=$(pgrep -x mysleep); echo "PID=$P"
PID=213
analyst@ubuntu-lab:~$ ls -l /proc/$P/exe
lrwxrwxrwx 1 analyst analyst 0 Sep 24 09:38 /proc/213/exe -> /home/analyst/mysleep
analyst@ubuntu-lab:~$ rm ~/mysleep; ls ~/mysleep
ls: cannot access '/home/analyst/mysleep': No such file or directory
analyst@ubuntu-lab:~$ ls -l /proc/$P/exe
lrwxrwxrwx 1 analyst analyst 0 Sep 24 09:38 /proc/213/exe -> '/home/analyst/mysleep (deleted)'
analyst@ubuntu-lab:~$ ps -o pid,stat,cmd -p $P
PID STAT CMD
213 S+ /home/analyst/mysleep 300
analyst@ubuntu-lab:~$ kill $P
| 관찰 | 의미 |
|---|---|
file 결과가 ELF 64-bit LSB pie executable | 디스크의 sleep은 실행 가능한 파일 일 뿐이다. 이 상태에서는 CPU도 메모리도 쓰지 않는다 |
| PID 194, 195 두 개 생성 | 같은 프로그램에서 독립된 프로세스 두 개 가 만들어졌다 |
두 프로세스 모두 exe=/usr/bin/sleep, ppid=169 | 같은 실행 파일, 같은 부모(내 bash)에서 왔다. 그러나 PID와 메모리(VmRSS 1944kB / 1988kB)는 각자 가진다 |
STAT S+ | S = 대기(sleep) 중, + = 터미널의 포그라운드 프로세스 그룹. 상태 코드는 04편에서 자세히 본다 |
kill 후 pgrep 결과 없음, 파일은 그대로 | 프로세스는 사라졌지만 프로그램은 디스크에 남는다 |
Ubuntu: rm 후에도 PID 213 유지 | 파일 이름만 사라졌고, 프로세스가 inode를 잡고 있어 실행은 계속된다 |
/proc/213/exe -> '/home/analyst/mysleep (deleted)' | 커널이 "원본 경로는 삭제됨"을 표시한다. 관제에서 매우 중요한 지표다 |
ls는 터미널로 출력할 때 공백이 들어간 이름을 작은따옴표로 감싸서 보여 준다(coreutils 8.25 이후 기본 동작). 그래서'... (deleted)'처럼 보인다. 스크립트에서readlink /proc/PID/exe로 읽으면 따옴표 없이/home/analyst/mysleep (deleted)가 나온다.
| 주제 | 내용 |
|---|---|
| 파일 삭제 ≠ 위협 제거 | 악성 파일을 지워도 이미 실행된 프로세스는 메모리에서 계속 동작 한다. 대응 순서는 "프로세스 정보 수집 → 종료 → 파일·지속성 제거"다 |
| 증거 복구 | 삭제된 실행 파일도 프로세스가 살아 있는 동안에는 cp /proc/PID/exe /evidence/sample.bin으로 복사해 낼 수 있다. 프로세스를 먼저 죽이면 이 기회가 사라진다 |
| 이름 위장 | 프로세스 이름(comm, cmdline)은 바꿀 수 있지만 /proc/PID/exe는 커널이 관리하는 실제 경로다. 이름보다 exe 경로를 신뢰 한다 (43편) |
| 권한 | 프로세스는 실행한 계정(UID)의 권한으로 동작한다. 같은 프로그램이라도 root로 실행하면 root 권한 프로세스가 된다 |
[Detection] EDR/스크립트 경보: /proc/*/exe 가 '(deleted)' 인 프로세스 발견
↓
[확인] ls -l /proc/PID/exe · tr '\0' ' ' < /proc/PID/cmdline · ps -o user,ppid,lstart -p PID
↓
[증거 확보] cp /proc/PID/exe → sha256sum → 샘플 보관
ls -l /proc/PID/fd · ss -tnp | grep "pid=PID"
↓
[판단] 패키지 업데이트 후 재시작 안 한 정상 프로세스인가? 아니면 /tmp·홈에서 실행 후 자기 삭제한 악성인가?
↓
[Response] 프로세스 종료 → 지속성(cron·systemd) 점검 → 보고
| 관점 | 내용 |
|---|---|
| 정상 오탐 | dnf update/apt upgrade로 라이브러리·바이너리가 교체된 뒤 재시작하지 않은 서비스도 (deleted)로 보인다. 경로가 /usr/...이고 패키지 업데이트 시각과 맞으면 정상일 가능성이 높다 |
| 고위험 | /tmp, /dev/shm, /var/tmp, 사용자 홈에서 실행된 뒤 삭제된 프로세스 |
| 기록 항목 | PID, PPID, UID, 시작 시각(lstart), exe, cmdline, 네트워크 연결, 해시 |
| 실수 | 결과 | 예방 |
|---|---|---|
악성 파일부터 rm | 프로세스는 살아 있고 샘플 확보 기회도 줄어든다 | 프로세스 정보·exe 복사 → 종료 → 삭제 순서 |
| 프로세스 이름만 보고 판단 | sshd, kworker로 위장한 프로세스를 놓친다 | /proc/PID/exe와 PPID를 함께 확인 |
ps 결과를 PID만 기록 | PID는 재사용되므로 나중에 다른 프로세스와 혼동 | PID + 시작 시각 + exe를 함께 기록 |
업데이트 후 (deleted)를 모두 악성으로 보고 | 과도한 오탐 | 경로·패키지 업데이트 이력과 교차 확인 |
[ ] ls -l, file 로 실행 파일(프로그램)의 정체를 확인했다
[ ] 같은 프로그램으로 PID가 다른 프로세스 2개를 만들었다
[ ] /proc/PID/exe 로 프로세스의 실제 실행 파일을 확인했다
[ ] 프로세스를 종료해도 프로그램 파일이 남는 것을 확인했다
[ ] 실행 중인 복사본을 삭제하고 '(deleted)' 표시를 확인했다
[ ] 파일 삭제가 위협 제거가 아닌 이유를 설명할 수 있다
/proc/PID/exe는 커널이 알려 주는 실제 실행 파일 경로 이며, 삭제되면 (deleted)가 붙는다.다음 글 「02. 프로세스 생성 원리 (fork·exec)」 에서는 셸이 명령어를 실행할 때 내부에서 일어나는 fork()(자기 복제)와 execve()(프로그램 교체)를 strace로 직접 추적 한다. exec가 PID를 바꾸지 않는다는 사실이 왜 보안 로그 분석에서 중요한지도 확인한다.