리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 37 / 50 · Part 4. 프로세스·서비스·리소스
실습 환경: Rocky Linux 9 (10.0.0.200) / Ubuntu 22.04 (10.0.0.210)
이전 글: 36. systemd 구조 이해
36편에서 systemd의 구조(Unit, 파일 위치, target, cgroup)를 봤다. 이번 글은 그 구조를 다루는 명령 systemctl을 정리한다.
systemctl start나 stop은 누구나 쓰지만, 관제에서 자주 틀리는 부분은 따로 있다.
active와 enabled를 같은 뜻으로 읽는 것restart와 reload를 구분하지 않는 것status의 Main PID ... code=killed, signal=KILL 같은 종료 원인 을 놓치는 것서비스가 멈췄을 때 이것이 장애인지, 사람의 조작인지, 공격인지 를 가르는 단서가 systemctl status 한 화면에 다 들어 있다.
| 축 | 명령 | 바뀌는 것 |
|---|---|---|
| 지금 (runtime) | start, stop, restart, reload | 현재 실행 여부 → Active |
| 부팅 시 (install) | enable, disable | *.wants/ 링크 → Loaded 줄의 enabled/disabled |
| 둘 다 | enable --now, disable --now | 한 번에 |
| 완전 차단 | mask, unmask | Unit을 /dev/null로 링크 → 수동 start도 불가 |
그래서 이런 조합이 모두 가능하다.
| Active | 부팅 설정 | 의미 |
|---|---|---|
| active | enabled | 정상 운영 중인 서비스 |
| active | disabled | 누군가 수동으로 켰다 → 재부팅하면 꺼짐 |
| inactive | enabled | 부팅 후 죽었거나 누군가 stop → 확인 필요 |
| inactive | masked | 의도적으로 차단 |
| 명령 | 동작 | PID | 연결 |
|---|---|---|---|
restart | 종료 후 다시 시작 | 바뀜 | 끊김 |
reload | 설정만 다시 읽음 (Unit의 ExecReload, 대개 SIGHUP) | 유지 | 유지 |
try-restart / reload-or-restart | 실행 중일 때만 / reload 불가하면 restart | ||
daemon-reload | Unit 파일 자체 를 systemd가 다시 읽음 | 서비스와 무관 |
Unit 파일을 수정하고 daemon-reload 없이 restart만 하면, systemd는 예전 설정 으로 서비스를 다시 띄운다(경고 메시지가 나온다).
| 상태 | 의미 |
|---|---|
active (running) | 실행 중 |
active (exited) | 일회성 작업이 성공하고 끝남 (Type=oneshot) |
inactive (dead) | 정지 |
activating (auto-restart) | 죽어서 재시작 대기 중 — 반복되면 크래시 루프 |
failed | 실패 상태로 멈춤. Result: 에 원인 |

systemctl은 스스로 서비스를 실행하지 않는다. D-Bus를 통해 PID 1(systemd)에 요청 을 보낼 뿐이다. 실제 fork·exec, cgroup 생성, 재시작 판단은 systemd가 한다. 그래서:
sudo가 필요하다.systemd[1]: Started ..., Stopped ... 형태로 남는다.status의 종료 원인 표기는 34편의 시그널·종료 코드와 그대로 이어진다.
| status 표기 | 의미 |
|---|---|
code=exited, status=0/SUCCESS | 정상 종료 |
code=exited, status=1/FAILURE | 프로그램 오류 종료 — 설정 오류가 흔함 |
code=killed, signal=TERM | SIGTERM으로 종료 (stop 이면 정상) |
code=killed, signal=KILL | SIGKILL — 강제 종료 또는 OOM Killer(39편) |
code=dumped, signal=SEGV | 크래시 (코어 덤프) |
Result: exit-code / signal / timeout / oom-kill | 실패 분류 |
# 1) 상태
systemctl status sshd
systemctl is-active sshd; systemctl is-enabled sshd; systemctl is-failed sshd
# 2) 조작
sudo systemctl restart httpd
sudo systemctl reload httpd # 지원 서비스만
sudo systemctl enable --now chronyd
sudo systemctl disable --now cups
sudo systemctl mask cups # 완전 차단
# 3) Unit 파일 수정 후
sudo systemctl edit sshd # 드롭인 생성 (/etc/systemd/system/sshd.service.d/override.conf)
sudo systemctl daemon-reload
# 4) 목록
systemctl --failed
systemctl list-units --type=service --state=running
systemctl list-unit-files --state=enabled
# 5) 서비스 로그 (48편)
journalctl -u sshd --since "1 hour ago" --no-pager
# 6) 한 번에 여러 속성
systemctl show auditd -p ActiveState,SubState,Result,ExecMainStatus,NRestarts,ActiveEnterTimestamp
아래 출력은 형식 설명용 예시다.
① 크래시 루프
Active: activating (auto-restart) (Result: exit-code) since ...
Process: 2211 ExecStart=/usr/sbin/httpd ... (code=exited, status=1/FAILURE)
...
httpd[2211]: AH00526: Syntax error on line 42 of /etc/httpd/conf/httpd.conf
status=1/FAILURE와 하단 로그의 설정 문법 오류 → 장애 다. 누가 언제 설정 파일을 바꿨는지(변경 관리, ls -l --time-style=full-iso)를 확인한다.
② 보안 서비스 강제 종료 (그림의 예)
Loaded: loaded (...auditd.service; enabled; ...)
Active: failed (Result: signal) since Tue 02:33:05 KST
Main PID: 812 (code=killed, signal=KILL)
| 단서 | 해석 |
|---|---|
enabled 인데 failed | 계속 떠 있어야 할 서비스가 멈춤 |
signal=KILL | 스스로 죽은 게 아니라 외부에서 SIGKILL (또는 OOM) |
| 시각 02:33 | 새벽, 변경 작업 일정 없음 |
OOM이었다면 journalctl -k에 Out of memory: Killed process 812 (auditd)가 남는다(39편). 그 기록이 없다면 사람(또는 악성코드)의 행위 로 보고 그 시각의 로그인·sudo 기록을 확인한다.
| 행위 | systemctl 흔적 | 의미 |
|---|---|---|
| 보안 서비스 중지 (T1562.001) | auditd, firewalld, EDR 에이전트 inactive / masked | 탐지 회피 |
| 방화벽 해제 | systemctl disable --now firewalld | 외부 접근 확대 (46편) |
| 악성 서비스 등록 | enable --now 후 새 Unit active | 지속성 (36편) |
| 서비스 설정 변조 | systemctl edit으로 드롭인 → ExecStart 교체 | 정상 서비스 이름으로 악성 실행 |
| 크래시 루프 | NRestarts 증가 | 장애 또는 공격 트래픽 |
auditd는 설계상 systemctl stop auditd를 거부하도록 되어 있다(RefuseManualStop=yes, service auditd stop을 사용). 그래서 공격자는 kill -9를 쓰는 경우가 많고, 이것이 ② 같은 signal=KILL 흔적으로 남는다.
for s in auditd rsyslog firewalld sshd crond; do
printf '%-10s %-9s %-9s %s\n' "$s" \
"$(systemctl is-active $s 2>/dev/null)" \
"$(systemctl is-enabled $s 2>/dev/null)" \
"$(systemctl show $s -p Result --value 2>/dev/null)"
done
Ubuntu에서는 firewalld 대신 ufw, crond 대신 cron, rsyslog는 동일하다.
journalctl _PID=1 --since today | grep -Ei 'Stopped|Started|Failed|killed' | tail -30
journalctl --since today | grep -E 'systemctl (stop|disable|mask)' # sudo 로 실행했다면 sudo 로그에도 남음
[Alert] SIEM: 서버 A audit 로그 수신 중단 02:33
↓
[status] auditd: enabled / failed / Main PID code=killed, signal=KILL
↓
[OOM?] journalctl -k 02:30~02:35 → Out of memory 기록 없음
↓
[누가] secure 02:31 devuser sudo 성공(ALL) → 02:33 kill -9 812 (sudo 로그 COMMAND=)
↓
[Response] auditd 재시작, devuser 세션 종료·잠금, 02:31 이후 행위 전수 조사
start/stop ↔ enable/disable.mask는 수동 시작까지 막는다. restart는 PID가 바뀌고, reload는 설정만 다시 읽는다.daemon-reload.status에서 볼 곳: Loaded 경로·enabled, Active 상태·시각, Main PID 종료 원인, 하단 로그.enabled + failed + signal=KILL → OOM 기록이 없으면 외부 강제 종료 를 의심한다.다음 글 「38. cron 작업 스케줄링」 에서는 정해진 시각에 명령을 실행하는 cron과 systemd timer를 다룬다. crontab 문법, 설정 파일 위치(사용자·시스템·cron.d), 실행 로그 위치(RHEL /var/log/cron, Ubuntu syslog), 그리고 cron을 이용한 지속성 탐지를 정리한다.