본문으로 건너뛰기

IT

Ⅱ-3. 프로세스와 systemd 서비스

라즈베리파이 5 서버에서 프로세스의 확인과 종료, 자원 사용, systemd 유닛의 구성, 서비스의 시작·중지·자동 실행, rc.local 과의 차이를 실제 실행 결과와 함께 정리하였다.

2026. 10. 10

Ⅱ-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
그림 1. 현재 터미널의 프로세스

옵션 없는 ps 는 지금 터미널(pts/0)에 묶인 프로세스만 보인다. 셸 bash 와 방금 실행한 ps 두 개이다. 표 1은 ps -f 가 더 보이는 열을 나타낸다 [1].

표 1. ps -f 출력의 열 (그림 1의 ps -f 줄)
열 값 의미
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
그림 2. PID 1 과 systemd 프로세스
  • $$ 는 지금 셸의 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의 값으로 그린 프로세스 나무이다.

PID 1 에서 갈라지는 프로세스 나무
그림 3. PID 1 에서 갈라지는 프로세스 나무 (도식)

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 
그림 4. top 의 요약 줄

표 2는 그림 4의 다섯 줄을 나타낸다 [2], [3].

표 2. top 요약 줄의 항목 (그림 4)
줄 값 의미
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
그림 5. CPU 사용률 상위 프로세스와 코어 수
  • 위의 두 프로세스는 이 사이트의 단어장 음성을 만드는 변환 작업이다. 사용자는 웹 서버 계정 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
그림 6. 신호 목록 (앞 15개)

표 3은 그림 6 가운데 프로세스를 끝낼 때 쓰는 신호 네 가지를 나타낸다 [5].

표 3. 프로세스 종료에 쓰는 신호
신호 번호 보내는 방법 프로세스의 처리
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
그림 7. SIGTERM 과 SIGKILL 로 프로세스 끝내기
  • 첫 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.target
그림 8. systemd 판과 기본 목표

systemd 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
그림 9. 종류별 유닛 수 (유닛 이름은 운영 서비스를 드러내므로 개수만 실었다)

표 4는 그림 9의 종류 가운데 이 연재에서 다루는 다섯 가지를 나타낸다 [8].

표 4. 유닛의 종류 (그림 9의 일부)
종류 이 서버의 수 관리하는 대상 다루는 글
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
그림 10. 연습용 유닛 파일 practice.service

표 5는 그림 10의 세 절을 나타낸다 [8], [10].

표 5. 유닛 파일의 절 (그림 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
active
그림 11. 서비스 시작과 상태
  • daemon-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
그림 12. enable·disable·stop
  • enable 은 default.target.wants 디렉토리에 유닛 파일을 가리키는 링크를 만들었다. 이 디렉토리 이름은 그림 10의 WantedBy=default.target 에서 나온다.
  • disable 은 그 링크를 지웠다. 유닛 파일 자체는 남는다.
  • disable 뒤에도 서비스는 계속 실행 중이었고, stop 을 실행하자 inactive 가 되었다.

표 6은 그림 11과 그림 12에서 쓴 systemctl 명령을 정리한 것이다. 시스템 서비스에서는 --user 를 빼고 앞에 sudo 를 붙인다 [9].

표 6. systemctl 의 서비스 관리 명령
명령 동작 확인 명령
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=no
그림 13. 사용자의 linger 설정

Linger=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
그림 14. rc.local 파일과 rc-local.service
  • 이 서버에는 /etc/rc.local 이 없다.
  • static 은 [Install] 절이 없어 enable 할 수 없는 유닛이라는 뜻이다 [9].
  • ConditionFileIsExecutable=/etc/rc.local 은 그 파일이 있고 실행 권한(Ⅱ-2 표 2의 x)이 있을 때만 유닛을 실행하는 조건이다 [8].
  • 유닛 파일의 주석에 따르면 /etc/rc.local 이 실행 가능할 때 systemd-rc-local-generator 가 이 유닛을 부팅 단계에 넣는다 [12].

표 7은 같은 명령을 부팅 때 실행하는 두 방법을 비교한 것이다.

표 7. rc.local 과 systemd 서비스 유닛의 비교
항목 /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. [1]Linux man-pages project, ps(1). [Online]. Available: https://man7.org/linux/man-pages/man1/ps.1.html [Accessed: 2026-10-10]
  2. [2]Linux man-pages project, top(1). [Online]. Available: https://man7.org/linux/man-pages/man1/top.1.html [Accessed: 2026-10-10]
  3. [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. [4]Linux man-pages project, nice(1). [Online]. Available: https://man7.org/linux/man-pages/man1/nice.1.html [Accessed: 2026-10-10]
  5. [5]Linux man-pages project, signal(7). [Online]. Available: https://man7.org/linux/man-pages/man7/signal.7.html [Accessed: 2026-10-10]
  6. [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. [7]systemd project, systemd(1). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemd.html [Accessed: 2026-10-10]
  8. [8]systemd project, systemd.unit(5). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemd.unit.html [Accessed: 2026-10-10]
  9. [9]systemd project, systemctl(1). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/systemctl.html [Accessed: 2026-10-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. [11]systemd project, loginctl(1). [Online]. Available: https://www.freedesktop.org/software/systemd/man/latest/loginctl.html [Accessed: 2026-10-10]
  12. [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]

ABOUT AUTHOR

shind

shind

소개글이 아직 없습니다.

댓글 남기기

Your email address will not be published. Required fields are marked *