
서비스 · 프로세스 관리 21 / 50 · Part 3. systemd와 서비스
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (systemd로 부팅한 Docker 격리 컨테이너, 테스트 계정analyst)
Part 3 「systemd와 서비스」를 시작한다. Part 1·2를 지나오는 동안 PID 1은 여러 번 등장했다. 모든 프로세스의 조상(03편), 고아의 새 부모(08편), 서비스를 멈추고 다시 띄우는 주체(13편), cgroup을 만드는 관리자(18편). 그 PID 1의 정체가 systemd 다.
이번 글에서는 PID 1이 어떤 프로그램인지 확인하고, systemd가 관리하는 대상(unit)의 전체 규모를 살펴본 뒤, systemctl이 PID 1과 어떻게 통신하는지, 사용자마다 따로 뜨는 systemd --user는 무엇인지 정리한다.
「리눅스 시스템 기초 8편(부팅 과정)」과 「36편(systemd 구조)」에서 개괄한 내용을, 이번 Part에서는 10편에 걸쳐 실무 수준으로 다룬다.
| 세대 | 방식 | 특징 |
|---|---|---|
| SysV init | /etc/init.d/ 셸 스크립트 + runlevel | 순차 시작, 단순하지만 느림 |
| Upstart | 이벤트 기반 | Ubuntu 6.10~14.10 |
| systemd | unit 파일 + 의존성 그래프 | 병렬 시작, cgroup, 로그·로그인·타이머 통합 (RHEL 7+, Ubuntu 15.04+) |
| 역할 | 내용 | 관련 편 |
|---|---|---|
| 부팅 | default.target까지 필요한 unit을 의존성 순서로 시작 | 27편 |
| 서비스 관리 | 시작·중지·재시작·상태 추적 | 23·24편 |
| 프로세스 관리 | 고아 입양, 좀비 회수 | 08편 |
| cgroup 관리 | 서비스·세션별 cgroup 생성, 자원 제한 | 18편 |
| 부가 데몬 | journald(로그), logind(로그인 세션), udevd(장치) | 31편 |
| 특수 동작 | PID 1이 종료되면 커널 패닉. 시그널도 명시적으로 처리하는 것만 받는다 | — |
systemd가 관리하는 모든 대상을 unit 이라 한다. 서비스(.service)만이 아니라 소켓, 타이머, 마운트, target 등 11종이 있다(22편).

systemctl restart atd
→ /run/systemd/private (또는 D-Bus system bus) 로 PID 1 에 요청
→ PID 1: 권한 확인(root 또는 polkit 허가) → atd.service 중지 → 시작
→ 결과(job 완료/실패)를 systemctl 에 반환
systemctl은 스스로 서비스를 띄우지 않는다. 요청만 보내는 클라이언트 다. 실제 프로세스를 fork·exec하는 것은 PID 1이다. 그래서 systemctl로 시작한 서비스의 부모는 항상 PID 1이고, 셸과는 관계가 없다(10편의 nohup 문제가 생기지 않는 이유).
# 1) PID 1 의 정체
ps -o pid,ppid,user,lstart,cmd -p 1
readlink /proc/1/exe; ls -l /sbin/init
systemctl --version | head -1
pstree -p 1 | head -12
# 2) systemd 가 관리하는 unit 규모
systemctl list-units --type=service --state=running --no-pager --no-legend
systemctl list-units --no-pager --no-legend | awk '{print $1}' | sed 's/.*\.//' | sort | uniq -c | sort -rn
systemctl is-system-running
ls -l /run/systemd/private /run/dbus/system_bus_socket
# 3) (Ubuntu, analyst) 사용자별 systemd
ps -o pid,ppid,user,cmd -C systemd
systemctl --user is-system-running
systemctl --user list-units --type=socket --no-pager --no-legend | head -5
loginctl user-status analyst --no-pager | head -8



텍스트 원본(실제 출력):
[root@rocky9-lab ~]# ps -o pid,ppid,user,lstart,cmd -p 1
PID PPID USER STARTED CMD
1 0 root Thu Sep 24 11:48:18 2026 /usr/sbin/init
[root@rocky9-lab ~]# readlink /proc/1/exe; ls -l /sbin/init
/usr/lib/systemd/systemd
lrwxrwxrwx 1 root root 22 Sep 16 04:40 /sbin/init -> ../lib/systemd/systemd
[root@rocky9-lab ~]# systemctl --version | head -1
systemd 252 (252-67.el9_8.6.rocky.0.1)
[root@rocky9-lab ~]# pstree -p 1 | head -12
systemd(1)-+-agetty(39)
|-anacron(2396)
|-atd(37)
|-crond(38)
|-dbus-broker-lau(41)---dbus-broker(46)
|-sshd(31)---sshd-session(2419)---sshd-session(2432)---bash(2433)-+-head(2463)
| `-pstree(2462)
|-systemd(2423)---(sd-pam)(2425)
|-systemd-journal(21)
`-systemd-logind(32)
[root@rocky9-lab ~]# systemctl list-units --type=service --state=running --no-pager --no-legend
atd.service loaded active running Deferred execution scheduler
crond.service loaded active running Command Scheduler
dbus-broker.service loaded active running D-Bus System Message Bus
getty@tty1.service loaded active running Getty on tty1
sshd.service loaded active running OpenSSH server daemon
systemd-journald.service loaded active running Journal Service
systemd-logind.service loaded active running User Login Management
user@0.service loaded active running User Manager for UID 0
[root@rocky9-lab ~]# systemctl list-units --no-pager --no-legend | awk '{print $1}' | sed 's/.*\.//' | sort | uniq -c | sort -rn
15 target
14 service
11 mount
7 slice
4 socket
2 timer
2 scope
2 path
1 device
[root@rocky9-lab ~]# systemctl is-system-running
running
[root@rocky9-lab ~]# ls -l /run/systemd/private /run/dbus/system_bus_socket
srw-rw-rw- 1 root root 0 Sep 24 11:48 /run/dbus/system_bus_socket
srwx------ 1 root root 0 Sep 24 11:48 /run/systemd/private
analyst@ubuntu-lab:~$ ps -o pid,ppid,user,cmd -C systemd
PID PPID USER CMD
1 0 root /sbin/init
860 1 analyst /usr/lib/systemd/systemd --user
analyst@ubuntu-lab:~$ systemctl --user is-system-running
running
analyst@ubuntu-lab:~$ systemctl --user list-units --type=socket --no-pager --no-legend | head -5
dbus.socket loaded active listening D-Bus User Message Bus Socket
analyst@ubuntu-lab:~$ loginctl user-status analyst --no-pager | head -8
analyst (1001)
Since: Thu 2026-09-24 12:10:30 UTC; 181ms ago
State: active
Sessions: *96
Linger: no
Unit: user-1001.slice
├─session-96.scope
│ ├─856 "sshd: analyst [priv]"
| 관찰 | 의미 |
|---|---|
PID 1 /usr/sbin/init, PPID 0 | 커널이 직접 실행했다. 명령 줄에는 init으로 보인다 |
readlink /proc/1/exe → /usr/lib/systemd/systemd | 실제 실행 파일은 systemd 다. /sbin/init은 호환용 심볼릭 링크 |
systemd 252 (252-67.el9_8.6...) | Rocky 9.8의 systemd 버전. 기능 차이를 확인할 때 버전이 중요하다 |
pstree -p 1에 agetty, atd, crond, sshd, journald, logind... | 모든 서비스가 PID 1의 직계 자식이다 |
systemd(2423)---(sd-pam) | root 로그인으로 생긴 사용자별 systemd 인스턴스 |
| running 서비스 8개 | 최소 설치 서버는 이 정도다. 운영 서버에서 이 목록이 기준선 이 된다 |
| unit 종류별 개수: target 15, service 14, mount 11 ... | 서비스는 전체 unit의 일부일 뿐이다. 타이머 2개, 소켓 4개도 부팅 후 자동 동작한다 |
is-system-running → running | 실패한 unit이 없는 정상 상태. 하나라도 실패하면 degraded |
/run/systemd/private 권한 srwx------ root | systemctl ↔ PID 1 전용 통신 소켓. root만 쓸 수 있다 |
/run/dbus/system_bus_socket srw-rw-rw- | 일반 사용자도 D-Bus로 요청할 수 있지만, 권한 있는 작업은 polkit이 막는다 |
Ubuntu systemd --user (PID 860, analyst) | analyst 로그인 시 생성된 사용자 서비스 관리자 |
loginctl: Unit: user-1001.slice, session-96.scope | 사용자 → slice, 로그인 → scope. 18편의 cgroup 구조 그대로다 |
Ubuntu의 analyst UID가 1001인 것은 Ubuntu 공식 이미지에
ubuntu계정(UID 1000)이 미리 있기 때문이다.
| 주제 | 내용 |
|---|---|
| 지속성의 목적지 | systemd에 unit을 등록하면 재부팅 후에도 PID 1이 자동 실행 한다. 공격자가 가장 선호하는 지속성 기법이다 (MITRE ATT&CK T1543.002) |
| 사용자 레벨 지속성 | root가 아니어도 ~/.config/systemd/user/에 unit을 두면 systemd --user가 실행한다. 일반 계정 탈취만으로 가능한 지속성 이다 (46편) |
| 제어 권한 | systemctl로 서비스를 제어하려면 root나 polkit 허가가 필요하다. sudoers에 systemctl 전체를 허용하면 사실상 root 권한을 준 것과 같다 |
| 무결성 | /sbin/init이나 systemd 바이너리가 바뀌면 부팅 전체가 장악된다. 패키지 무결성 검증(rpm -V systemd) 대상이다 |
[기준선 수집 — 평소에]
systemctl list-units --type=service --state=running --no-legend > baseline_running.txt
systemctl list-unit-files --state=enabled --no-legend > baseline_enabled.txt
↓
[점검 — 정기적으로 또는 경보 시]
diff baseline_running.txt <(systemctl list-units --type=service --state=running --no-legend)
diff baseline_enabled.txt <(systemctl list-unit-files --state=enabled --no-legend)
↓
[판단] 새로 enabled 된 unit, 설명(Description)이 비어 있거나 그럴듯한 이름(systemd-update-*)
↓
[확인] systemctl cat <unit> · 실행 파일 경로 · 생성 시각 (46편)
| 관점 | 확인 |
|---|---|
| 시스템 상태 | systemctl is-system-running이 degraded면 실패 unit 확인 |
| 사용자 manager | ps -C systemd -o user,cmd로 어떤 계정의 사용자 systemd가 떠 있는지 |
| linger | loginctl show-user USER -p Linger — yes면 로그아웃 후에도 사용자 서비스가 계속 실행된다 |
| 실수 | 결과 | 예방 |
|---|---|---|
/etc/init.d/ 스크립트만 찾음 | systemd 서버에서 서비스 정의를 놓침 | systemctl cat, unit 파일 경로 확인 |
| systemd 서비스 프로세스를 kill로 조작 | systemd가 재시작하거나 상태가 어긋남 | systemctl 사용 |
degraded를 무시 | 실패한 서비스(보안 에이전트일 수도)를 방치 | systemctl --failed 확인 |
| 사용자 systemd를 점검에서 제외 | 사용자 레벨 지속성 누락 | systemctl --user와 홈 디렉터리 unit 점검 |
[ ] PID 1 의 실제 실행 파일이 systemd 인 것을 확인했다
[ ] systemctl --version 으로 버전을 확인했다
[ ] 실행 중인 서비스 목록(기준선)을 저장했다
[ ] unit 종류별 개수를 집계했다
[ ] /run/systemd/private 소켓의 권한을 확인했다
[ ] systemd --user 인스턴스와 user slice / session scope 를 확인했다
systemd --user가 따로 동작한다.다음 글 「22. Unit 종류와 Unit 파일 위치」 에서는 11가지 unit 종류와, unit 파일이 놓이는 여러 디렉터리의 우선순위 를 정리한다. /usr/lib, /etc, /run 중 어디의 파일이 이기는지, enabled·static·alias 같은 상태가 무엇을 뜻하는지 확인한다.