데몬화 절차 (Daemonization)

Ⅰ. 개요

1. 정의

**데몬(Daemon)**은 백그라운드에서 지속적으로 실행되는 프로세스로, 터미널과 분리되어 사용자의 직접적인 상호작용 없이 시스템 서비스를 제공한다. 데몬화는 일반 프로세스를 데몬으로 전환하는 일련의 절차이다.

데몬의 특징
┌─────────────────────────────────────────────────┐
│                                                 │
│  일반 프로세스             데몬 프로세스         │
│  ┌──────────┐            ┌──────────┐          │
│  │ 터미널   │             │          │          │
│  │ 제어     │             │ 터미널   │          │
│  │ 종속     │   ──→      │ 독립     │          │
│  │          │  데몬화    │          │          │
│  │ PID=PPID │             │ PID=1    │          │
│  │ (부모)   │             │ (init)   │          │
│  └──────────┘            └──────────┘          │
│                                                 │
│  특징:                                          │
│  - 제어 터미널 없음 (no controlling terminal)   │
│  - 부모 프로세스가 init(1) 또는 systemd(1)       │
│  - 백그라운드에서 24시간 실행                    │
│  - 예: sshd, httpd, crond, syslogd              │
└─────────────────────────────────────────────────┘

비유: 데몬화는 "부모님(터미널) 집에서 독립해서 자기 집(백그라운드)을 가지는 것"과 같다. 부모님이 나가도(터미널이 닫혀도) 자기 일을 계속 해나가는 성숙한 어른 프로세스가 되는 과정이다.

Ⅱ. 전통적 데몬화 절차

1. 전체 절차 다이어그램

데몬화 7단계 절차
┌──────────────────────────────────────────────────────────┐
│                                                          │
│  1. fork() ──── 부모는 exit(), 자식이 계속               │
│       │                                                  │
│       ▼                                                  │
│  2. setsid() ── 새 세션 생성 (세션 리더)                 │
│       │         제어 터미널 분리                          │
│       ▼                                                  │
│  3. fork() ──── 2번째 fork (세션 리더 방지)              │
│       │                                                  │
│       ▼                                                  │
│  4. chdir("/") ── 작업 디렉토리를 루트로 변경            │
│       │          (파일시스템 언마운트 방지)               │
│       ▼                                                  │
│  5. umask(0) ── 파일 생성 마스크 초기화                  │
│       │                                                  │
│       ▼                                                  │
│  6. close(0~2) ─ 표준 입출력 파일 디스크립터 닫기       │
│       │                                                    │
│       ▼                                                  │
│  7. redirect ── stdin/stdout/stderr를 /dev/null로 재지정  │
│                                                          │
└──────────────────────────────────────────────────────────┘

2. 각 단계 상세 설명

단계별 상태 변화
┌─────────────────────────────────────────────────────────┐
│                                                         │
│  [1단계: fork()]                                        │
│  ┌─────────┐  fork()   ┌─────────┐                     │
│  │ 부모    │ ────────→ │ 부모    │ exit(0) → 소멸      │
│  │ PID=100 │           │ 자식    │ → 데몬 후보          │
│  └─────────┘           │ PID=101 │                      │
│                        └─────────┘                      │
│  목적: 터미널에서 백그라운드로 분리                      │
│                                                         │
│  [2단계: setsid()]                                      │
│  자식(PID=101):                                         │
│  - 새 세션 생성 → 세션 리더가 됨                         │
│  - 새 프로세스 그룹 생성 → 그룹 리더가 됨               │
│  - 제어 터미널과의 연결 자동 해제                        │
│                                                         │
│  [3단계: 2번째 fork()]                                   │
│  PID=101: fork() → PID=102                              │
│  PID=101: exit(0) → 소멸                                │
│  PID=102: 계속 실행                                      │
│  목적: 세션 리더가 아니게 되어                           │
│        터미널을 다시 획득할 가능성 원천 차단              │
│                                                         │
└─────────────────────────────────────────────────────────┘

3. 나머지 단계 (4~7)

나머지 데몬화 단계
┌─────────────────────────────────────────────────────────┐
│                                                         │
│  [4단계: chdir("/")]                                    │
│  - 작업 디렉토리를 /로 변경                              │
│  - 이유: 데몬이 특정 파일시스템에 있으면                 │
│         해당 파일시스템을 언마운트할 수 없음              │
│                                                         │
│  [5단계: umask(0)]                                      │
│  - 파일 권한 마스크를 0으로 리셋                         │
│  - 이유: 상속받은 마스크로 인해 데몬이                   │
│         파일 생성 시 의도치 않은 권한 제한 발생 방지     │
│                                                         │
│  [6단계: 파일 디스크립터 닫기]                           │
│  - close(STDIN_FILENO);   // fd 0                       │
│  - close(STDOUT_FILENO);  // fd 1                       │
│  - close(STDERR_FILENO);  // fd 2                       │
│                                                         │
│  [7단계: /dev/null로 재지정]                             │
│  - fd 0 = open("/dev/null", O_RDONLY)                    │
│  - fd 1 = open("/dev/null", O_WRONLY)                    │
│  - fd 2 = open("/dev/null", O_WRONLY)                    │
│  - 이유: 터미널 없이 출력/입력 시 오류 방지              │
│                                                         │
└─────────────────────────────────────────────────────────┘

Ⅲ. 데몬화 코드 예시

1. C 언어 구현

int daemonize(void) {
    // 1. 첫 번째 fork
    pid_t pid = fork();
    if (pid < 0) return -1;
    if (pid > 0) exit(0);  // 부모 종료

    // 2. 새 세션 생성
    if (setsid() < 0) return -1;

    // 3. 두 번째 fork (세션 리더 방지)
    pid = fork();
    if (pid < 0) return -1;
    if (pid > 0) exit(0);  // 첫 번째 자식 종료

    // 4. 작업 디렉토리 변경
    chdir("/");

    // 5. 파일 마스크 리셋
    umask(0);

    // 6-7. 파일 디스크립터 닫기 및 재지정
    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
    open("/dev/null", O_RDONLY);  // stdin → /dev/null
    open("/dev/null", O_WRONLY);  // stdout → /dev/null
    open("/dev/null", O_WRONLY);  // stderr → /dev/null

    return 0;
}

Ⅳ. systemd를 이용한 대안

1. systemd 서비스 유닛 파일

systemd 서비스 파일 구조
┌──────────────────────────────────────────────────┐
│                                                    │
│  /etc/systemd/system/mydaemon.service              │
│  ┌──────────────────────────────────────────┐     │
│  │ [Unit]                                   │     │
│  │ Description=My Custom Daemon              │     │
│  │ After=network.target                     │     │
│  │                                          │     │
│  │ [Service]                                │     │
│  │ Type=simple          ← 포그라운드 실행   │     │
│  │ ExecStart=/usr/bin/mydaemon              │     │
│  │ Restart=on-failure   ← 실패 시 재시작   │     │
│  │ User=daemon          ← 실행 사용자      │     │
│  │ Group=daemon         ← 실행 그룹        │     │
│  │                                          │     │
│  │ [Install]                               │     │
│  │ WantedBy=multi-user.target               │     │
│  └──────────────────────────────────────────┘     │
│                                                    │
│  관리 명령어:                                      │
│  $ systemctl start mydaemon    ← 시작             │
│  $ systemctl stop mydaemon     ← 중지             │
│  $ systemctl enable mydaemon   ← 부팅 시 자동 시작│
│  $ systemctl status mydaemon   ← 상태 확인        │
│                                                    │
└──────────────────────────────────────────────────┘

2. 전통적 데몬화 vs systemd

┌───────────────────┬──────────────────┬──────────────────┐
│      특징         │   전통적 데몬화  │   systemd 서비스  │
├───────────────────┼──────────────────┼──────────────────┤
│ fork 필요 여부    │  2번 fork 필수   │  불필요          │
│ 세션 관리         │  setsid() 직접  │  systemd가 관리  │
│ 로깅             │  syslog 직접    │  journald 통합   │
│ 의존성 관리       │  수동            │  자동 (After=)   │
│ 재시작 정책       │  직접 구현       │  Restart= 설정   │
│ 리소스 제한       │  수동            │  LimitCPU= 등    │
│ PID 파일 관리     │  직접 생성       │  PIDFile=        │
│ 수명 추적         │  제한적          │  cgroup 기반     │
└───────────────────┴──────────────────┴──────────────────┘

Ⅴ. 지식 그래프 및 요약

1. 지식 그래프

[데몬화 절차]
├── [전통적 방식 (7단계)]
│   ├── fork() ──────── 부모 종료, 백그라운드 분리
│   ├── setsid() ────── 새 세션, 제어 터미널 해제
│   ├── fork() ──────── 세션 리더 방지
│   ├── chdir("/") ──── 작업 디렉토리 루트로
│   ├── umask(0) ────── 권한 마스크 초기화
│   ├── close fds ───── 표준 입출력 닫기
│   └── redirect ────── /dev/null로 재지정
├── [현대적 방식]
│   ├── systemd service ── Type=simple (포그라운드)
│   ├── systemd service ── Type=forking (전통적 호환)
│   └── systemd service ── Type=notify (sd_notify)
├── [데몬 예시]
│   ├── sshd ──────── SSH 서버
│   ├── httpd/nginx ── 웹 서버
│   ├── crond ──────── 스케줄러
│   └── syslogd ────── 시스템 로그
└── [주의사항]
    ├── 시그널 처리 (SIGHUP, SIGTERM)
    ├── 로그 기록 (syslog, journald)
    └── PID 파일 생성 (/var/run/*.pid)

2. 준말

  • PPID: Parent Process ID (부모 프로세스 식별자)
  • TTL: Controlling Terminal (제어 터미널)

3. 어린이를 위한 3줄 설명

데몬은 컴퓨터가 켜져 있는 동안 조용히 백그라운드에서 일하는 프로세스예요. 부모 프로세스와 터미널에서 독립해서, 화면이 꺼져도 계속 일할 수 있게 만드는 것이 "데몬화"예요. 요즘은 systemd가 이 복잡한 과정을 알아서 해줘서 더 쉽게 데몬을 만들 수 있어요.