Daemon

Daemon

Daemon은 백그라운드에서 사용자와의 interaction 없이 동작하는 프로그램이다. 대표적인 데몬 프로세스 형태의 프로그램은 웹서버를 예로 들 수 있다.

daemon의 운영하는 방식은 아래와 같이 2가지가 있다.

  • standalone
    • 시스템에 독자적으로 프로세스가 구동되어 서비스를 제공하는 방식으로 백그라운드에서 항상 동작해야하는 경우에 사용된다. 부팅시부터 메모리에 상주되어 동작하므로 자주 호출되고 항상 대기하고 있어야하는 데몬의 경우 사용된다.
    • 예) apache, mySql, name server
  • super daemon(xinetd, systemd)
    • 슈퍼 데몬에 의해서 구동이 되는 방식으로 서비스 요청이 오면 슈퍼 데몬이 해당 데몬을 동작시킨다. 서비스 응답속도는 느릴 수 있지만 항상 메모리에 상주된 형태가 아니여서 자원을 효율적으로 사용한다.
    • 예) telnet, shell, ftp

daemon 기본 구조 만들기

daemon 프로세스를 만들때 일반 실행 파일 형식으로 작성해도 동작은 가능하지만 아래와 같은 구조를 기본적으로 가지는 것이 좋다.

1. fork 함수를 이용해 부모 프로세스 종료

데몬을 작성할 떄는 코드의 대부분이 에러체크 구문일 정도로 최대한 방어적으로 작성해야하는데 특히 fork 호출 직후에는 에러 체크를 주의해야한다.

pid_t pid;

pid = fork();
if (pid < 0) {          // 자식 프로세스를 생성하지 못하는 경우에 데몬 종료
    exit(EXIT_FAILURE);
}

if (pid > 0) {          // 부모 프로세스는 여기서 종료시킨다.
    exit(EXIT_SUCCESS);
}

2. 파일 권한 설정(mask) 변경

로그 파일과 같이 데몬에 의해 생성되는 파일에 대한 읽기,쓰기,실행권한 설정을 한다. 아래와 같이 ‘0’으로 설정을 하면 모든 접근 권한을 가지게 된다.

그리고 파일 권한 설정을 하고 로그 작성을 위한 파일을 열어서 에러 체크시마다 혹은 상태 변화가 생길 때마다 로그를 작성하는 것이 좋다. 데몬은 디버깅이 쉽지 않으므로 로그 메세지도 최대한 자세하게 작성해야한다.

umask(0);

3. Session 만들기

자식 프로세스가 올바르게 동작하기 위해서 커널로부터 고유한 세션 ID를 발급받아 프로세스를 세션 리더로 만든다.

pid_t sid;

sid = setsid();
if (sid < 0) {
    exit(EXIT_FAILURE);
}

4. 작업 디렉토리 변경

많은 리눅스 배포판이 리눅스 파일 시스템 구조를 온전히 따르지 않고 저마다 차이가 있을 수 있어 가장 확실한 경로인 root(‘/’)로 작업 디렉토리를 변경한다.

if ((chdir("/")) < 0) {
    exit(EXIT_FAILURE);
}

5. STDIN/STDOUT/STDERR 닫기

daemon 프로세스는 터미널을 사용하지 않기 때문에 standard file descriptor는 불필요할 뿐만 아니라 잠재적인 보안 위험요소이기 때문에 닫아줘야 한다.

close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);

6. 데몬의 실제 기능을 구현한다.

간단하게 while로 무한루프를 만들어서 그 안에 작성을 한다.

while (1) 
{
    /* Do some task here ... */
    
    sleep(30); /* wait 30 seconds */
}

Daemon을 Systemd 등록하기

Systemd는 System V init daemon을 대체하여 centOS 7 이후부터 나온 슈퍼데몬이다. Systemd는 서비스의 시작을 병렬화하여 부팅 시간을 상당히 단축시킨 보다 발전된 형태라고 한다.

Systemd에 daemon을 등록하기 위해 .service 파일을 아래와 같이 작성하도록 한다.

service file 작성

service file은 크게 [Unit], [Service], [Install] 3가지 섹션으로 구성된다.

▼ testService.service

[Unit]
Description=Test Daemon
After=syslog.target

[Service]
Enviroment=NODE_ENV=production PORT=8808
ExecStart=/usr/sbin/test_daemon
Restart=always
RestartSec=10
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=test-daemon

[Install]
WantedBy=multi-user.target

[Unit]

설정 항목을 작성하는 부분

  • Description : 유닛에 대한 설명
  • After, Before : 유닛이 실행되는 순서 명시

[Service]

  • Environment : 환경변수 설정
  • ExecStart : 서비스가 실행을 위한 커맨드
  • Restart : 서비스가 활성화된 경우에 서비스가 종료되었을 때 재시작할 수 있도록 함
  • RestartSec : 재시작되기까지 대기시간 (기본값:100ms)
  • StandardOutput, StandardError, SyslogIdentifier : 로그메세지 출력과 관련된 옵션

[Install]

  • WantedBy, RequiredBy : 해당 유닛의 서비스는 명시된 런레벨 상태에서 실행되도록 하는 옵션; ‘multi-user.target’은 텍스트 모드의 다중 사용자 모드이다.

service 관리

서비스를 설정하고 실행한다.

$ sudo systemctl daemon-reload
$ sudo systemctl enable testService
$ sudo systemctl start testService

서비스가 제대로 실행되고 있는지 상태를 확인한다.

$ sudo systemctl status testService

서비스의 목록을 확인한다.

$ sudo systemctl list-units

서비스의 로그 메세지를 확인한다.

$ journalctl -u testService

[참고]

https://web.archive.org/web/20061118065514/http://www.linuxprofilm.com/articles/linux-daemon-howto.html#s1 http://wiki.hash.kr/index.php/%EB%8D%B0%EB%AA%AC https://documentation.suse.com/sled/15-SP1/html/SLED-all/cha-systemd.html https://www.shellhacks.com/systemd-service-file-example/

Categories:

Updated: