Ⅱ-3 프로세스와 systemd 서비스는 Ⅱ-2 사용자·그룹과 권한에서 파일에 붙인 소유자와 권한이 실행 중인 프로그램에도 붙는다는 것을 이어서 다룬다. 명령의 출력은 genealogypost.com 을 운영하는 라즈베리파이 5에서 2026-10-10 에 일반 사용자 계정으로 실행한 결과이다. 서비스 실습은 일반 사용자의 연습용 유닛 하나로만 하였고, 다 쓴 뒤 그 유닛 파일을 지웠다. 출력에 든 사용자 이름은 user 로, 호스트 이름은 raspberrypi 로, 홈 디렉토리 경로는 /home/user 로 바꾸어 실었다.
3.1 프로세스 및 PID
3.1.1 현재 터미널의 프로세스
프로세스는 실행 중인 프로그램 하나이다. 같은 프로그램을 두 번 실행하면 프로세스가 둘 생긴다. 커널은 프로세스마다 번호(PID, Process ID)를 붙이고, 그 프로세스를 만든 부모의 번호(PPID)를 함께 기록한다 [1]. 그림 1은 ps 와 ps -f 로 지금 터미널에서 실행 중인 프로세스를 확인한 결과이다.
user@raspberrypi:~ $ ps
PID TTY TIME CMD
999834 pts/0 00:00:00 bash
999838 pts/0 00:00:00 ps
user@raspberrypi:~ $ ps -f
UID PID PPID C STIME TTY TIME CMD
user 999834 999832 0 01:10 pts/0 00:00:00 bash -i
user 999849 999834 0 01:10 pts/0 00:00:00 ps -f옵션 없는 ps 는 지금 터미널(pts/0)에 묶인 프로세스만 보인다. 셸 bash 와 방금 실행한 ps 두 개이다. 표 1은 ps -f 가 더 보이는 열을 나타낸다 [1].
| 열 | 값 | 의미 |
|---|---|---|
| UID | user | 프로세스를 실행한 사용자. 이 사용자의 권한으로 파일에 접근한다(Ⅱ-2 2.2절) |
| PID | 999849 | 프로세스 번호 |
| PPID | 999834 | 부모 프로세스의 번호. ps 의 부모는 bash 이다 |
| C | 0 | CPU 사용률(%)의 정수값 |
| STIME | 01:10 | 시작 시각 |
| TTY | pts/0 | 묶인 터미널. 터미널이 없으면 ? 로 표시한다 |
| TIME | 00:00:00 | 지금까지 쓴 CPU 시간 |
| CMD | ps -f | 실행한 명령과 인자 |
ps 의 PPID 999834 는 bash 의 PID 와 같다. 다시 말해 셸에서 입력한 명령은 셸의 자식 프로세스로 실행된다.
3.1.2 PID 1 과 프로세스 나무
부모를 따라 올라가면 모든 프로세스는 PID 1 에 닿는다. 그림 2는 PID 1 과 지금 셸의 정보, 그리고 이름이 systemd 인 프로세스를 확인한 결과이다.
user@raspberrypi:~ $ ps -o pid,ppid,user,ni,cmd -p $$,1
PID PPID USER NI CMD
1 0 root 0 /sbin/init splash
999834 999832 user 0 bash -i
user@raspberrypi:~ $ ps -o pid,user,cmd -C systemd
PID USER CMD
1 root /sbin/init splash
957 user /lib/systemd/systemd --user$$는 지금 셸의 PID 로 바뀐다. 그림 1의 bash 와 같은 999834 이다.- PID 1 은 root 가 실행한
/sbin/init이다.-C systemd로 이름이 systemd 인 프로세스를 찾았을 때 PID 1 이 나왔으므로, /sbin/init 의 실체는 systemd 이다. - PID 957 은 사용자 user 의 systemd 이다. 3.5절의 사용자 서비스는 이 프로세스가 관리한다.
그림 3은 그림 1, 그림 2, 그리고 뒤의 그림 7과 그림 11의 값으로 그린 프로세스 나무이다.
3.2 자원 사용 확인
3.2.1 top 의 요약 줄
top 은 프로세스의 자원 사용을 계속 갱신하며 보인다. -b 는 화면 갱신 없이 글자로 내보내고, -n1 은 한 번만 내보낸다 [2]. 그림 4는 그 출력의 요약 다섯 줄이다.
user@raspberrypi:~ $ top -bn1 | head -5
top - 01:11:15 up 44 days, 2:25, 5 users, load average: 9.65, 9.47, 9.30
Tasks: 322 total, 4 running, 318 sleeping, 0 stopped, 0 zombie
%Cpu(s): 12.5 us, 25.0 sy, 62.5 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 8063.0 total, 228.4 free, 3876.6 used, 4124.1 buff/cache
MiB Swap: 512.0 total, 0.7 free, 511.2 used. 4186.4 avail Mem 표 2는 그림 4의 다섯 줄을 나타낸다 [2], [3].
| 줄 | 값 | 의미 |
|---|---|---|
| 1. 시각과 부하 | up 44 days, load average 9.65, 9.47, 9.30 | 부팅 뒤 44일이 지났다. 부하 평균은 1분·5분·15분 동안 실행 중이거나 실행을 기다리는 프로세스 수의 평균이다 |
| 2. 프로세스 수 | 322 total, 4 running | 전체 322개 가운데 4개가 실행 중이고 318개는 잠든 상태이다 |
| 3. CPU 시간 비율 | 12.5 us, 25.0 sy, 62.5 ni, 0.0 id | us 는 사용자 프로그램, sy 는 커널, ni 는 우선순위를 낮춘 프로그램, id 는 쉬는 시간의 비율(%)이다 |
| 4. 메모리 | 8063.0 total, 3876.6 used, 4124.1 buff/cache | MiB 단위. buff/cache 는 파일 내용을 담아 둔 메모리로, 필요하면 비운다 |
| 5. 스왑 | 512.0 total, 511.2 used, 4186.4 avail Mem | 스왑은 메모리 내용을 옮겨 두는 디스크 공간이다. avail Mem 은 스왑 없이 새 프로그램에 줄 수 있는 메모리이다 |
3.2.2 부하 평균의 해석
부하 평균은 CPU 코어 수와 견주어 읽는다. 이 서버의 코어 수는 nproc 출력으로 4 이다(그림 5). 1분 부하 평균을 코어 수로 나누면 다음과 같다.
- 9.65 / 4 = 2.41
이는 코어 하나에 평균 2.41 개의 프로세스가 실행 중이거나 차례를 기다렸음을 의미한다. 1분·5분·15분 값이 9.65, 9.47, 9.30 으로 비슷하므로 이 부하는 15분 넘게 이어진 것으로 판단된다. 그림 5는 그 시각에 CPU 를 가장 많이 쓴 프로세스를 확인한 결과이다.
user@raspberrypi:~ $ ps -eo pid,user,ni,pcpu,comm --sort=-pcpu | head -4
PID USER NI %CPU COMMAND
962066 www-data 19 169 toeic-voice.py
962067 www-data 19 168 toeic-voice.py
999999 user 0 100 ps
user@raspberrypi:~ $ nproc
4- 위의 두 프로세스는 이 사이트의 단어장 음성을 만드는 변환 작업이다. 사용자는 웹 서버 계정 www-data 이다.
- %CPU 는 프로세스가 시작된 뒤 쓴 CPU 시간을 지난 시간으로 나눈 값이며, 코어 하나를 다 쓰면 100 이다 [1]. 169 는 평균 코어 1.69 개만큼 썼다는 뜻이다.
- NI 19 는 우선순위를 가장 낮춘 값이다. nice 값은
-20부터 19 까지이며, 클수록 다른 프로세스에 CPU 를 먼저 내준다 [4].
그림 4의 ni 62.5% 는 이 두 프로세스가 쓴 CPU 시간이다. 두 작업은 nice 19 로 실행되므로, 웹 서버처럼 nice 0 인 프로세스가 CPU 를 요청하면 그쪽이 먼저 실행된다.
3.3 신호와 프로세스 종료
프로세스를 끝낼 때는 신호(signal)를 보낸다. 신호는 커널이 프로세스에 전달하는 번호 붙은 알림이다 [5]. 그림 6은 kill -l 로 신호 목록의 앞 15개를 확인한 결과이다.
user@raspberrypi:~ $ kill -l | head -3
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM표 3은 그림 6 가운데 프로세스를 끝낼 때 쓰는 신호 네 가지를 나타낸다 [5].
| 신호 | 번호 | 보내는 방법 | 프로세스의 처리 |
|---|---|---|---|
| SIGHUP | 1 | 터미널을 닫을 때 | 기본 동작은 종료. 설정 파일을 다시 읽도록 처리하는 서비스가 있다 |
| SIGINT | 2 | Ctrl+C | 기본 동작은 종료. 프로그램이 가로채어 처리할 수 있다 |
| SIGKILL | 9 | kill -KILL |
바로 종료. 가로채거나 무시할 수 없다 |
| SIGTERM | 15 | kill (신호를 적지 않을 때) |
기본 동작은 종료. 프로그램이 가로채어 정리 작업을 한 뒤 끝낼 수 있다 |
그림 7은 sleep 300 을 뒤에서 실행한 뒤 SIGTERM 과 SIGKILL 로 각각 끝낸 결과이다. 명령 끝의 & 는 명령을 뒤에서(background) 실행하고 셸 프롬프트를 바로 돌려준다. %1 은 셸의 작업 번호 1 을 가리킨다 [6].
user@raspberrypi:~ $ sleep 300 &
[1] 999882
user@raspberrypi:~ $ jobs -l
[1]+ 999882 Running sleep 300 &
user@raspberrypi:~ $ kill -TERM %1
user@raspberrypi:~ $ jobs -l
[1]+ 999882 Terminated sleep 300
user@raspberrypi:~ $ sleep 300 &
[1] 999911
user@raspberrypi:~ $ kill -KILL %1
user@raspberrypi:~ $ jobs -l
[1]+ 999911 Killed sleep 300- 첫 sleep 은 PID 999882 로 실행되었고 SIGTERM 을 받아 Terminated 로 끝났다.
- 둘째 sleep 은 PID 999911 로 실행되었고 SIGKILL 을 받아 Killed 로 끝났다.
- sleep 은 신호를 가로채지 않으므로 두 경우 모두 바로 끝났다. 정리 작업이 있는 프로그램은 SIGTERM 을 먼저 보내고, 끝나지 않을 때만 SIGKILL 을 보낸다.
- 일반 사용자는 자기 프로세스에만 신호를 보낼 수 있다. 그림 5의 www-data 프로세스를 끝내려면 Ⅱ-2 2.5절의 sudo 가 필요하다 [5].
3.4 systemd 및 유닛
systemd 는 PID 1 로 실행되어 부팅 과정과 서비스를 관리하는 프로그램이다 [7]. 그림 8은 이 서버의 systemd 판과 부팅 때 도달하는 목표(target)를 확인한 결과이다.
user@raspberrypi:~ $ systemctl --version | head -1
systemd 252 (252.39-1~deb12u2)
user@raspberrypi:~ $ systemctl get-default
graphical.targetsystemd 252 는 Debian 12 의 꾸러미 판이다(Ⅱ-1 그림 2). 기본 목표 graphical.target 은 데스크톱 화면까지 띄우는 상태이며, Ⅰ-2 에서 Desktop 판 OS 를 설치하였기 때문이다.
systemd 가 관리하는 대상 하나를 유닛(unit)이라 한다. 유닛의 종류는 파일 이름의 확장자로 구분한다 [8]. 그림 9는 지금 불러온 유닛을 종류별로 센 결과이다.
user@raspberrypi:~ $ systemctl list-units --all --no-legend --plain | awk '{print $1}' | sed 's/.*.//' | sort | uniq -c | sort -rn
151 service
106 device
50 target
22 mount
16 timer
16 socket
8 slice
8 scope
5 path
1 swap
1 automount표 4는 그림 9의 종류 가운데 이 연재에서 다루는 다섯 가지를 나타낸다 [8].
| 종류 | 이 서버의 수 | 관리하는 대상 | 다루는 글 |
|---|---|---|---|
| service | 151 | 프로그램 하나의 시작·중지 | 이번 편 |
| target | 50 | 여러 유닛을 묶은 부팅 단계(graphical.target 따위) | 이번 편 |
| mount | 22 | 파일시스템의 연결 | Ⅱ-5 저장장치와 파일시스템 |
| timer | 16 | 정해진 시각에 다른 유닛 실행 | Ⅵ-3 예약 작업과 셸 스크립트 |
| path | 5 | 파일이 바뀌면 다른 유닛 실행 | Ⅵ-3 예약 작업과 셸 스크립트 |
3.5 서비스 시작·중지·상태
3.5.1 유닛 파일의 구성
서비스 실습에는 사용자 유닛을 썼다. 사용자 유닛은 그림 2의 PID 957 이 관리하며, 일반 사용자가 sudo 없이 시작하고 멈출 수 있다. 시스템 유닛과 명령은 같고, --user 옵션과 유닛 파일의 위치만 다르다 [9]. 그림 10은 연습용 유닛 파일을 만들고 내용을 확인한 결과이다.
user@raspberrypi:~ $ mkdir -p ~/.config/systemd/user && printf '[Unit]nDescription=practicen[Service]nExecStart=/bin/sleep 600n[Install]nWantedBy=default.targetn' > ~/.config/systemd/user/practice.service
user@raspberrypi:~ $ cat ~/.config/systemd/user/practice.service
[Unit]
Description=practice
[Service]
ExecStart=/bin/sleep 600
[Install]
WantedBy=default.target표 5는 그림 10의 세 절을 나타낸다 [8], [10].
| 절 | 항목 | 뜻 |
|---|---|---|
| [Unit] | Description=practice | status 출력에 보일 설명 |
| [Service] | ExecStart=/bin/sleep 600 | 서비스를 시작할 때 실행할 명령. 600초 동안 기다리는 명령으로 서비스를 대신하였다 |
| [Install] | WantedBy=default.target | enable 할 때 이 유닛을 default.target 에 묶는다. 사용자 유닛의 default.target 은 사용자의 systemd 가 시작될 때 도달하는 목표이다 |
3.5.2 시작 및 상태 확인
그림 11은 유닛 파일을 다시 읽게 한 뒤 서비스를 시작하고 상태를 확인한 결과이다.
user@raspberrypi:~ $ systemctl --user daemon-reload
user@raspberrypi:~ $ systemctl --user start practice
user@raspberrypi:~ $ systemctl --user status practice --no-pager
● practice.service - practice
Loaded: loaded (/home/user/.config/systemd/user/practice.service; disabled; preset: enabled)
Active: active (running) since Sat 2026-10-10 01:10:21 KST; 21ms ago
Main PID: 999572 (sleep)
Tasks: 1 (limit: 9577)
CPU: 1ms
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/practice.service
└─999572 /bin/sleep 600
Oct 10 01:10:21 raspberrypi systemd[957]: Started practice.service - practice.
user@raspberrypi:~ $ systemctl --user is-active practice
activedaemon-reload는 유닛 파일을 새로 만들거나 고친 뒤 systemd 가 다시 읽게 한다.- Loaded 줄은 유닛 파일의 위치와 자동 실행 여부(disabled)를 보인다. 아직 enable 하지 않았으므로 disabled 이다.
- Active 줄의 active (running) 은 실행 중이라는 뜻이다.
- Main PID 999572 는 ExecStart 의 sleep 이다. 마지막 줄의 systemd[957] 은 그림 2의 사용자 systemd 가 이 서비스를 시작하였음을 보인다.
- CGroup 줄의 경로 user@1000.service 의 1000 은 Ⅱ-2 그림 1의 uid 이다.
3.6 부팅 시 자동 실행
start 는 지금 한 번 실행하는 명령이고, enable 은 다음 부팅부터 자동으로 실행되게 하는 명령이다. 둘은 서로 독립이어서, enable 만 하면 지금은 실행되지 않는다 [9]. 그림 12는 enable 과 disable, stop 의 결과이다.
user@raspberrypi:~ $ systemctl --user enable practice
Created symlink /home/user/.config/systemd/user/default.target.wants/practice.service → /home/user/.config/systemd/user/practice.service.
user@raspberrypi:~ $ systemctl --user is-enabled practice
enabled
user@raspberrypi:~ $ systemctl --user disable practice
Removed "/home/user/.config/systemd/user/default.target.wants/practice.service".
user@raspberrypi:~ $ systemctl --user is-enabled practice
disabled
user@raspberrypi:~ $ systemctl --user stop practice
user@raspberrypi:~ $ systemctl --user is-active practice
inactive- enable 은 default.target.wants 디렉토리에 유닛 파일을 가리키는 링크를 만들었다. 이 디렉토리 이름은 그림 10의
WantedBy=default.target에서 나온다. - disable 은 그 링크를 지웠다. 유닛 파일 자체는 남는다.
- disable 뒤에도 서비스는 계속 실행 중이었고, stop 을 실행하자 inactive 가 되었다.
표 6은 그림 11과 그림 12에서 쓴 systemctl 명령을 정리한 것이다. 시스템 서비스에서는 --user 를 빼고 앞에 sudo 를 붙인다 [9].
| 명령 | 동작 | 확인 명령 |
|---|---|---|
| start · stop | 지금 시작하고 멈춘다 | is-active (active · inactive) |
| restart | 멈춘 뒤 다시 시작한다 | is-active |
| enable · disable | 부팅 때 자동 실행을 켜고 끈다 | is-enabled (enabled · disabled) |
| status | 상태와 최근 기록을 보인다 | 그림 11 |
| daemon-reload | 유닛 파일을 다시 읽는다 | status 의 Loaded 줄 |
사용자 유닛의 자동 실행은 시스템 부팅이 아니라 그 사용자의 systemd 가 시작될 때 일어난다. 사용자의 systemd 는 그 사용자가 로그인할 때 시작되고, 마지막 세션이 끝나면 멈춘다. 로그인 없이 부팅 때부터 실행하려면 linger 를 켜야 한다 [11]. 그림 13은 이 계정의 linger 설정이다.
user@raspberrypi:~ $ loginctl show-user $(whoami) -p Linger
Linger=noLinger=no 이므로 이 계정의 사용자 유닛은 로그인해야 시작된다. 시스템 유닛은 이 설정과 관계없이 부팅 때 실행된다. 실습을 마친 뒤 practice.service 파일을 지우고 daemon-reload 를 실행하였다.
3.7 rc.local 및 systemd 비교
systemd 이전의 리눅스는 부팅 마지막에 /etc/rc.local 스크립트를 실행하였다. 이 파일에 명령을 적으면 부팅 때 실행되었다. systemd 는 이 방식을 rc-local.service 라는 호환 유닛으로 이어받았다. 그림 14는 이 서버의 rc.local 과 그 유닛을 확인한 결과이다.
user@raspberrypi:~ $ ls /etc/rc.local
ls: cannot access '/etc/rc.local': No such file or directory
user@raspberrypi:~ $ systemctl is-enabled rc-local
static
user@raspberrypi:~ $ systemctl cat rc-local --no-pager | grep -E 'Condition|ExecStart|^After|^# /'
# /lib/systemd/system/rc-local.service
ConditionFileIsExecutable=/etc/rc.local
After=network.target
ExecStart=/etc/rc.local start
# /usr/lib/systemd/system/rc-local.service.d/debian.conf
After=network-online.target- 이 서버에는 /etc/rc.local 이 없다.
- static 은 [Install] 절이 없어 enable 할 수 없는 유닛이라는 뜻이다 [9].
ConditionFileIsExecutable=/etc/rc.local은 그 파일이 있고 실행 권한(Ⅱ-2 표 2의 x)이 있을 때만 유닛을 실행하는 조건이다 [8].- 유닛 파일의 주석에 따르면 /etc/rc.local 이 실행 가능할 때 systemd-rc-local-generator 가 이 유닛을 부팅 단계에 넣는다 [12].
표 7은 같은 명령을 부팅 때 실행하는 두 방법을 비교한 것이다.
| 항목 | /etc/rc.local | systemd 서비스 유닛 |
|---|---|---|
| 실행 조건 | 파일이 있고 실행 권한이 있을 때(그림 14) | enable 하였을 때(그림 12) |
| 실행 사용자 | root | 유닛 파일의 User= 로 정한다 |
| 상태 확인 | rc-local.service 하나로만 보인다 | 서비스마다 status 로 본다(그림 11) |
| 멈춘 프로그램의 재시작 | 하지 않는다 | Restart= 로 정한다 [10] |
| 실행 순서 | network-online.target 뒤(그림 14의 debian.conf 의 After=) | After= 로 유닛마다 정한다 |
rc.local 에 적은 명령은 모두 root 로 한꺼번에 실행되며, 실패해도 다시 실행되지 않는다. 이 서버는 /etc/rc.local 을 쓰지 않는다(그림 14).
다음 글은 Ⅱ-4. 패키지 관리와 업데이트 이다.
REFERENCES
- [1]Linux man-pages project, ps(1). [Online]. Available: https://man7.org/linux/man-pages/man1/ps.1.html [Accessed: 2026-10-10]
- [2]Linux man-pages project, top(1). [Online]. Available: https://man7.org/linux/man-pages/man1/top.1.html [Accessed: 2026-10-10]
- [3]Linux man-pages project, proc_loadavg(5). [Online]. Available: https://man7.org/linux/man-pages/man5/proc_loadavg.5.html [Accessed: 2026-10-10]
- [4]Linux man-pages project, nice(1). [Online]. Available: https://man7.org/linux/man-pages/man1/nice.1.html [Accessed: 2026-10-10]
- [5]Linux man-pages project, signal(7). [Online]. Available: https://man7.org/linux/man-pages/man7/signal.7.html [Accessed: 2026-10-10]
- [6]Free Software Foundation, GNU Bash Reference Manual, Job Control Basics. [Online]. Available: https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html [Accessed: 2026-10-10]
- [7]systemd project, systemd(1). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemd.html [Accessed: 2026-10-10]
- [8]systemd project, systemd.unit(5). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html [Accessed: 2026-10-10]
- [9]systemd project, systemctl(1). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html [Accessed: 2026-10-10]
- [10]systemd project, systemd.service(5). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemd.service.html [Accessed: 2026-10-10]
- [11]systemd project, loginctl(1). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/loginctl.html [Accessed: 2026-10-10]
- [12]Linux man-pages project, systemd-rc-local-generator(8). [Online]. Available: https://man7.org/linux/man-pages/man8/systemd-rc-local-generator.8.html [Accessed: 2026-10-10]