· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
daemon

지은이 : kukuta@gmail.com 참고 : Advenced Programming in Unix Environment

1. Daemon 이란?

Daemon프로세스는 시스템 시작이 시작할 때 그 생명을 시작하여, 우리가 알지 못하는 백그라운드에서 자신의 할일을 묵묵히 행하다, 시스템과 함께 그 생명을 다한다. 이 페이지에서는 Daemon의 특징과 간단한 Daemon 프로세스를 만들어 보도록 하겠다.

2. Deamon의 특징

일반적으로 Daemon을 단순히 시스템 백그라운드에서 돌아가고 있는 프로세스라 생각하기 쉽다. 아래의 화면을 보고 Daemon과 일반 백그라운 프로세스의 차이를 알아 보도록 하자. (가장 밑에 있는 프로세스가 필자가 띄운 백그라운드 프로세스다)
[Test]$ ps -axj | more
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
    1     4     1     1 ?           -1 SW       0   0:04 [keventd]
    1     7     1     1 ?           -1 SW       0   8:25 [kswapd]
 2182  2382  2382  2182 pts/13    2400 S      528   0:00 ./test_proc
위의 결과는 test_proc라는 무한 루프를 도는 프로그램을 백그라운드로 실행 시키고 난후, 현재 시스템 위에서 돌아가고 있는 프로세스들의 PPID와 PID, PGID를 출력 해본 것이다. 결과에서 알 수 있다시피 Daemon들은 첫째 TTY(터미널 장치)를 가지고 있지 않다. 둘째 PPID(parent id)가 1로 세팅되어 있으며 SID(session id)역시 자신의 아이디와 같다.

3. Coding rule

위에서 언급 했듯이 Daemon은 일반 프로세스와는 다른 몇 가지 특징을 가지고 있다고 했다. Daemon을 만들기 전에 Daemon의 특징을 자세히 집고 넘어 가도보록 하자.

3.1. 파일을 만들땐 umask 설정!

설명을 하기전에 아래의 결과 화면을 먼저 보도록 하자.
[Test]$ umask
0002
[Test]$ touch test_umask
[Test]$ ls -la
합계 8
drwxrwxr-x    2 kukuta   kukuta       4096 10월 17 21:36 .
drwxr-xr-x    9 kukuta   kukuta       4096 10월 17 21:14 ..
-rw-rw-r--    1 kukuta   kukuta          0 10월 17 21:36 test_umask
[Test]$ umask 0
[Test]$ touch test_umask_zero
[Test]$ ls -la
합계 8
drwxrwxr-x    2 kukuta   kukuta       4096 10월 17 21:37 .
drwxr-xr-x    9 kukuta   kukuta       4096 10월 17 21:14 ..
-rw-rw-r--    1 kukuta   kukuta          0 10월 17 21:36 test_umask
-rw-rw-rw-    1 kukuta   kukuta          0 10월 17 21:37 test_umask_zero
만일 Daemon이 파일을 생성하는데, 그 파일이 아무도 읽을 수도, 수정 할 수도 없는 파일이라면 문제가 있는 것이다. Daemon이 파일을 생성해야 할 필요 성이 있다면 umask 함수를 사용하여 Daemon이 생성하는 파일의 접근 권한을 미리 설정 해 두자.

3.2. fork()를 이용하여 PPID를 1로!

위에서 언급했다 싶이 Daemon의 특징 중에 하나가 PPID가 1(init)라는 것이다. 여기서 우리가 기억해야 할 것은, 한 프로세스가 자식 프로세스를 만들고, 자식 프로세스가 소멸 되기도 전에 부모가 죽어 버린다면, 자식 프로세스는 init프로세스에게 입양되며, 그 init의 PID가 1이라는 것이다. 그리고 fork()가 될경우 자식 프로세스는 새로운 프로세스 아이디를 받지만, 그룹 아이디는 부모의 것을 그대로 상속 받게 된다. 한마디로 그룹의 리더가 아니라는 말이다. 이렇게 부모 프로세스를 죽이는 것은 다음에 나올 그룹의 리더가 되기 위한 setsid()호출의 필수불가결한 조건이다. (비정한 프로세스의 세상이다. 그룹의 리더가 되기 위해 부모를 죽이다니...ㅠㅠ)

3.3. 새로운 Session을위해 setsid()를..

setsid 함수는 호출하는 프로세스가 그룹의 리더가 아닐때 새로운 세션을 생성하여 다음과 같은 세 가지 일을 한다.
  1. 호출한 프로세스는 새로운 세션의 리더가 된다.
  2. 호출한 프로세스는 새로운 그룹의 리더가 된다.
  3. 프로세스는 컨트롤 터미널을 잃어 버리게 된다(언제나 얻을 수만은 없다)
※ session 이란 ?
하나 또는 이상의 프로세스 그룹들의 집합이다.

3.4. 새로운 디렉토리를 찾아서..

chdir을 이용하여 현재 워킹 디렉토리를 루트 디렉토리(/)로 변경한다. 부모로 물려 받은 워킹 디렉토리는 파일 시스템에 마운트 되어 있는 것일 수도 있고, 시스템이 정지 할때 까지 살아 있는 Daemon의 특성 때문에 파일 시스템이 언마운트 되지 않을 수도 있다.

3.5. 필요 하지 않은 파일 디스크립터는 모두 죽인다

Daemon의 특징 중에 하나는 컨트롤 터미널을 가지지 않는 것이다. 이는 시스템이 시작할 때 아무도 몰래 실행되어 시스템이 죽을 때 그 운명을 같이 하는 Daemon을 사용자들이 궂이 애써 알필요도 없고, 컨트롤 해줘야 할 필요도 없는 것이기 때문이다. Daemon에게 여러 필요 없는 파일 디스크립터들을 물린다는 것은 자원의 낭비요, 행여 모를 오류의 원인이 된다.

3.6. 죽지 않는 파일 디스크립터는 /dev/null에 물린다

Daemon은 터미널 디바이스 자체를 가지고 있지 않다. 그렇기에 0, 1, 2 번 파일 디스크립터 처럼, 터미널과 통신을 하는 것들은 아무런 영향을 줄 수 없는 /dev/null로 보내 버린다.

4. Daemonize function

#include <fcntl.h>
#include <iostream>
#include <signal.h>
#include <string>
#include <syslog.h>
#include <unistd.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>

void daemonize(const char* cmd)
{
    /*
     * set file creation mask to 0
     */
    umask(0);

    /*
     * Get maximum number of file descriptors
     */
    rlimit rl;
    if(getrlimit(RLIMIT_NOFILE, &rl) < 0) {
        std::cerr << "error getlimit" << std::endl;
    }

    pid_t pid;
    /*
     * Become a session leader to loase controlling TTY
     */
    if((pid = fork()) < 0)
    {
        std::cerr << "error fork" << std::endl;
    }
    else if(pid != 0) // parent process
    {
        exit(0);
    }
    setsid();

    struct sigaction sa;
    /*
     * ensure future open won't allocate controlling TTYs.
     */
    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    if(sigaction(SIGHUP, &sa, NULL) < 0)
    {
        std::cerr << "can't ignore SIGHUP" << std::endl;
    }
    
    if(chdir("/") < 0)
    {
        std::cerr << "can't change directory " << std::endl;
    }

    /*
     * Close all file descriptors
     */
    if(rl.rlim_max == RLIM_INFINITY)
    {
        rl.rlim_max = 1024;
    }
    for(int i=0; i<rl.rlim_max; i++)
    {
        close(i);
    }

    /*
     * Attach file descriptors 0, 1, and 2 to /dev/null
     */
    int fd0, fd1, fd2;
    fd0 = open("/dev/null", O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    /*
     * Initialize the log file
     */
    openlog(cmd, LOG_CONS, LOG_DAEMON);

    if(fd0 != 0 || fd1 != 1 || fd2 != 2)
    {
        syslog(LOG_ERR, "unexpected file descriptors %d %d %d", fd0, fd1, fd2);
        exit(1);
    }
    closelog();
}




sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2008-07-30 05:35:05
Processing time 0.0076 sec