| 구분 | 있는 곳 | 상태 |
|---|
| 프로그램 | 디스크 | 실행되기 전의 파일 |
| 프로세스 | 메모리 | 실행 중인 상태 |
- 하나의 프로그램에서 여러 개의 프로세스가 만들어질 수 있다. 터미널 창을 세 개 열면 bash 프로세스가 세 개다.
- 프로세스마다 고유한 번호(PID) 가 붙는다.
ls -l /usr/bin/bash
# -rwxr-xr-x. 1 root root 1150584 ... /usr/bin/bash ← 프로그램: 디스크의 파일 하나
ps -e | grep bash
# 2314 pts/0 00:00:00 bash ← 프로세스: 지금 실행 중인 것 둘
# 2850 pts/1 00:00:00 bash
- 파일은 하나인데 프로세스는 둘이다. 프로세스는 "프로그램이 실행된 한 번 한 번" 이다.
- PID는 만들어진 순서대로 붙고, 프로세스가 끝나면 그 번호는 나중에 다시 쓰인다. 같은 프로그램을 다시 실행해도 PID는 달라진다.
- 커널은 실행 중인 프로세스의 정보를
/proc/PID번호/ 디렉터리로 보여 준다. ls /proc 에 숫자 이름의 디렉터리가 잔뜩 보이는 이유다.
systemd (PID 1)
│ fork
├── sshd
│ └── bash ← 내가 로그인한 셸
│ ├── vi
│ └── ls
└── crond
| 용어 | 뜻 |
|---|
| PID | 프로세스 자신의 번호 |
| PPID | 부모 프로세스의 번호 |
| fork | 부모가 자신을 복제해 자식을 만드는 것 |
| exec | 그 자리에서 다른 프로그램으로 바뀌는 것 |
- 셸에서 명령을 실행하면 셸이 fork로 자식을 만들고 그 자식이 exec로 그 명령이 된다.
- PID 1은 모든 프로세스의 조상이다. 부팅 때 커널이 가장 먼저 띄운다.
| 시기 | PID 1 |
|---|
| 예전 | init |
| 지금 | systemd (또는 upstart) |
ps -ef | head -3
# UID PID PPID C STIME TTY TIME CMD
# root 1 0 0 09:00 ? 00:00:02 /usr/lib/systemd/systemd
# root 2 0 0 09:00 ? 00:00:00 [kthreadd]
- PID 1의 PPID는 0이다. 사람이 띄운 것이 아니라 커널이 직접 시작했다는 뜻이다.
- 이름이 대괄호로 싸인 것(
[kthreadd])은 커널 스레드다. 디스크의 프로그램을 실행한 것이 아니라 커널 내부의 일꾼이다.
- systemd를 쓰는 배포판에서
/sbin/init 은 systemd로 가는 심볼릭 링크다. 이름은 남아 있지만 실체가 바뀌었다.
echo $$ # 2314 ← 지금 셸의 PID
bash # 자식 셸을 하나 띄운다
echo $$ # 2901 ← 새 PID 를 받았다
echo $PPID # 2314 ← 부모는 방금 그 셸이다
exit # 자식을 끝내고 부모로 돌아온다
| 구분 | fork | exec |
|---|
| PID | 새 번호를 받는다 | 그대로다 |
| 원래 프로세스 | 부모로 남는다 | 새 프로그램으로 덮여 사라진다 |
| 프로세스 수 | 하나 늘어난다 | 변함없다 |
- 셸이
ls를 실행할 때는 fork로 자기 복사본을 만든 뒤, 그 복사본이 exec로 ls가 된다. 그래서 ls가 끝나도 셸은 남아 있다.
- 터미널에서
exec ls 라고 치면 fork 없이 셸 자신이 ls로 바뀐다. ls가 끝나면 돌아갈 셸이 없으므로 터미널이 닫힌다.
| 상태 | 무엇 |
|---|
| 고아(orphan) 프로세스 | 부모가 먼저 죽은 자식. PID 1이 입양한다 |
| 좀비(zombie) 프로세스 | 자식은 끝났는데 부모가 종료 상태를 안 받아간 상태 |
- 좀비는 이미 끝난 프로세스다. CPU도 메모리도 쓰지 않고 프로세스 목록의 자리만 차지한다.
ps에서 상태가 Z 로 보인다.
- 좀비는 직접 죽일 수 없다. 이미 죽어 있기 때문이다. 부모를 종료하면 PID 1이 정리한다.
ps -ef | grep defunct
# user 3120 3100 0 10:15 pts/0 00:00:00 [myapp] <defunct>
- 자식이 끝나면 커널은 "어떻게 끝났는지"(종료 상태)를 부모가 가져갈 때까지 보관한다. 그 보관 중인 상태가 좀비다.
ps에는 <defunct> 로 표시된다.
- 그래서 모든 프로세스는 끝날 때 잠깐 좀비를 거친다. 부모가 곧바로 거두면 눈에 띄지 않는다. 문제가 되는 것은 부모가 거두지 않아 계속 남는 경우다.
- 위 출력에서 좀비의 PID는 3120, 부모는 3100이다. 손댈 대상은 3120이 아니라 3100이다.
| 구분 | 고아 프로세스 | 좀비 프로세스 |
|---|
| 먼저 끝난 쪽 | 부모 | 자식 |
| 자식의 상태 | 살아서 실행 중 | 이미 끝났다 |
| 쓰는 자원 | 보통 프로세스와 같다 | 프로세스 목록의 한 칸뿐 |
ps에서 | PPID가 1로 바뀐다 | 상태 Z, <defunct> |
| 조치 | 필요 없다 — PID 1이 맡아 끝나면 거둔다 | 부모가 거두게 하거나 부모를 종료 |
- 고아는 정상적으로 일하는 프로세스다. 부모만 바뀌었을 뿐이다. 반면 좀비는 일하지 않는 빈 껍데기다.
- 시험의 답은 "PID 1이 입양한다"이다. 최근 배포판에서는 사용자 세션을 맡은 systemd 프로세스가 대신 새 부모가 되어 PPID가 1이 아닌 값으로 보이기도 한다.
- 좀비 하나는 해가 없지만, 계속 쌓이면 PID를 다 써서 새 프로세스를 만들지 못하게 된다.
| 구분 | 성격 |
|---|
| 포그라운드(foreground) | 터미널을 붙잡고 실행. 끝날 때까지 다른 명령을 못 친다 |
| 백그라운드(background) | 뒤에서 실행. 바로 다음 명령을 칠 수 있다 |
sleep 100 & # 처음부터 백그라운드로
jobs # 백그라운드 작업 목록
fg %1 # 1번 작업을 포그라운드로
bg %1 # 1번 작업을 백그라운드로
| 조작 | 하는 일 |
|---|
& | 명령 끝에 붙여 백그라운드 실행 |
Ctrl+Z | 실행 중인 작업을 일시 정지 |
Ctrl+C | 실행 중인 작업을 종료 |
jobs | 작업 목록 보기 |
fg | 포그라운드로 전환 |
bg | 정지된 작업을 백그라운드로 재개 |

Ctrl+Z는 정지이고 Ctrl+C는 종료다. 이 둘을 바꿔 쓰면 안 된다.
nohup 을 붙이면 터미널을 닫아도 계속 실행된다 — nohup 명령 &.
sleep 300 &
# [1] 3301 ← [작업 번호] PID
vi memo.txt # 편집하다가 Ctrl+Z
# [2]+ Stopped vi memo.txt
jobs
# [1]- Running sleep 300 &
# [2]+ Stopped vi memo.txt
jobs -l
# [1]- 3301 Running sleep 300 &
# [2]+ 3305 Stopped vi memo.txt
fg # 번호를 생략하면 + 가 붙은 작업(vi)이 온다
kill %1 # 작업 번호로 종료
| 표시 | 뜻 |
|---|
[1] | 작업 번호 — 그 셸 안에서만 통하는 번호 |
3301 | PID — 시스템 전체에서 통하는 번호 |
+ | 현재 작업 — fg·bg에 번호를 안 주면 이것이 대상 |
- | 그다음 순서의 작업 |
Running / Stopped / Done | 실행 중 / 정지 / 끝남 |
- 작업 번호에는
%를 붙인다. kill 1 은 PID 1에, kill %1 은 1번 작업에 시그널을 보낸다. 전혀 다른 대상이다.
jobs는 현재 셸이 띄운 작업만 보여 준다. 다른 터미널에서 실행한 것은 ps로 찾는다.
Ctrl+Z로 멈춘 작업은 끝난 것이 아니다. 목록에 Stopped로 남아 있으며 bg를 해야 다시 돈다.
| 키 | 프로세스에 가는 것 | 결과 |
|---|
Ctrl+C | SIGINT (2) | 종료 |
Ctrl+Z | SIGTSTP (20) | 정지 — 다시 이어 갈 수 있다 |
Ctrl+\ | SIGQUIT (3) | 종료 |
Ctrl+D | 시그널이 아니다 — 입력의 끝(EOF) | 셸에서는 로그아웃 |
nohup ./backup.sh &
# [1] 3410
# nohup: ignoring input and appending output to 'nohup.out'
| 실행 방법 | 터미널을 붙잡나 | 터미널을 닫으면 |
|---|
명령 | 붙잡는다 | 종료된다 |
명령 & | 안 붙잡는다 | 종료될 수 있다 |
nohup 명령 & | 안 붙잡는다 | 계속 실행된다 |
- 터미널이 닫히면 거기서 띄운 프로세스들에게 SIGHUP(1) 이 간다.
nohup(no hang up)은 이 시그널을 무시하게 해서 살아남게 한다.
nohup만으로는 백그라운드가 되지 않는다. 뒤로 보내는 것은 &의 몫이라 둘을 함께 쓴다.
- 화면이 없어지므로 출력은 현재 디렉터리의
nohup.out 파일에 쌓인다.
- 백그라운드 작업이라도 출력은 터미널에 그대로 찍힌다. 섞이지 않게 하려면
명령 > out.log 2>&1 & 처럼 파일로 돌린다.
- 백그라운드에서 요청을 기다리는 서비스 프로세스다. 터미널에 붙어 있지 않다.
- 이름이
d로 끝나는 관례가 있다.
| 데몬 | 하는 일 |
|---|
httpd | 웹 서버 |
sshd | 원격 접속(SSH) |
crond | 예약 작업 |
syslogd | 로그 기록 |
ftpd | 파일 전송 |
named | DNS |
ps -ef | grep -E "sshd|crond"
# root 1023 1 0 09:00 ? 00:00:00 /usr/sbin/sshd -D
# root 1045 1 0 09:00 ? 00:00:00 /usr/sbin/crond -n
- TTY 칸이
? 다. 연결된 터미널이 없다는 뜻이며 데몬을 알아보는 표시다. 내가 터미널에서 띄운 프로세스는 pts/0 처럼 나온다.
- PPID가 1이다. 사용자가 로그인해서 띄운 것이 아니라 부팅 때 PID 1이 시작했다.
d로 끝나는 것은 관례일 뿐 규칙이 아니다. nginx·sendmail처럼 d로 끝나지 않는 데몬도 있고, 반대로 find·pwd·useradd는 d로 끝나도 데몬이 아니다.
| 구분 | 백그라운드 프로세스 | 데몬 |
|---|
| 누가 띄우나 | 사용자가 &로 | 부팅 때 시스템이 |
| 터미널 | 연결되어 있다 (pts/0) | 없다 (?) |
| 언제 끝나나 | 일이 끝나거나 터미널이 닫힐 때 | 시스템이 꺼지거나 중지시킬 때까지 |
- systemd를 쓰는 배포판에서 데몬은
systemctl start sshd · systemctl stop sshd 처럼 다룬다. 이전 방식은 service httpd start 였다.
| 방식 | 성격 |
|---|
| standalone | 항상 메모리에 상주하며 대기. 응답이 빠르다. 자주 쓰이는 서비스에 적합 |
| inetd (xinetd) | 슈퍼 데몬이 대신 대기하다가 요청이 오면 그때 띄운다. 메모리를 아낀다 |
- standalone은 빠르고 메모리를 많이, inetd 방식은 느리지만 메모리를 아낀다. 이 맞바꿈이 시험에 나온다.
inetd를 개선한 것이 xinetd 이며 접근 제어 기능이 추가되었다.
- 요청이 잦은 웹·SSH는 standalone, 어쩌다 쓰는 telnet 같은 것은 inetd 방식에 어울린다.
standalone 요청 ──────────────────────► httpd (늘 떠 있다)
inetd 방식 요청 ──► xinetd (늘 떠 있다) ──► in.telnetd (그때 띄운다)
└ 여러 서비스의 문을 혼자 지킨다
| 구분 | inetd | xinetd |
|---|
| 설정 | /etc/inetd.conf 파일 하나 | /etc/xinetd.conf + 서비스별 파일을 두는 /etc/xinetd.d/ |
| 접근 제어 | 자체 기능 없음 | 자체 기능 있음 |
# /etc/xinetd.d/telnet
service telnet
{
disable = no
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.telnetd
}
disable = no 가 "사용한다" 는 뜻이다. "끄기 = 아니오"라는 이중 부정이라 헷갈린다. disable = yes 면 그 서비스는 꺼진다.
- 슈퍼 데몬 자신(xinetd)은 standalone으로 돈다. 늘 떠 있어야 다른 서비스를 대신 기다릴 수 있다.
- "슈퍼 데몬"이라는 이름은 여러 데몬을 거느리고 대신 깨워 준다는 데서 왔다. 권한이 더 높다는 뜻이 아니다.
내일은 이 프로세스를 눈으로 보고 다루는 명령 — ps, top, kill과 작업 예약을 다룬다.
- 프로세스 : 실행되어 메모리에 올라간 프로그램.
- PID / PPID : 프로세스 자신의 번호 / 부모 프로세스의 번호.
- fork : 부모 프로세스가 자신을 복제해 자식 프로세스를 만드는 동작.
- 고아 프로세스 : 부모가 먼저 종료된 자식 프로세스. PID 1이 입양한다.
- 좀비 프로세스 : 종료되었으나 부모가 종료 상태를 회수하지 않아 목록에 남은 프로세스.
- 포그라운드 / 백그라운드 : 터미널을 붙잡고 실행 / 뒤에서 실행.
- 데몬 : 배후에서 서비스 요청을 기다리는 프로세스. 이름이 주로
d로 끝난다.
- standalone / inetd : 데몬을 항상 상주시키는 방식 / 슈퍼 데몬이 필요할 때 띄우는 방식.
📝 연습 문제
선택지를 클릭하면 정답·해설이 펼쳐집니다.
다음 중 부모 프로세스가 종료 상태를 회수하지 않아 프로세스 목록에만 남아 있는 프로세스로 알맞은 것은?
다음 중 리눅스에서 모든 프로세스의 조상이 되는 PID 1번 프로세스로 알맞은 것은?
다음 중 명령을 처음부터 백그라운드로 실행하기 위해 명령 끝에 붙이는 기호로 알맞은 것은?
다음 중 실행 중인 포그라운드 작업을 일시 정지시키는 키 조합으로 알맞은 것은?
다음 중 서비스 데몬을 항상 메모리에 상주시켜 요청에 빠르게 응답하도록 하는 실행 방식으로 알맞은 것은?