Help us understand the problem. What is going on with this article?

Linuxでプロセスのデーモン化

More than 5 years have passed since last update.

プロセスと周辺の概念

TTY (仮想端末)
 |-セッション
   |-プロセスグループ
     |-プロセス

「TTY = セッション 1>N プロセスグループ 1>N プロセス」

プロセスの中の一つがセッションリーダーで、その中の複数がプロセスグループリーダー。

セッション

TTYと結びついたログインセッションのこと。一つ以上のプロセスグループの集まりであり、セッションリーダーとなるプロセスが制御端末とやりとりをして端末切断時に子プロセスを全て終了させる役割を持つ。自身が属するセッション内でしかプロセスグループは生成できない。

プロセスグループ

プロセスの集まりで、シグナルを他のプロセスへ送信することができる。メンバーはプロセスグループリーダーのプロセスIDをグループIDとして持つ。プロセスグループのリーダーになるにはsetpgid()かsetsid()を呼び出す。

fork()

この関数を呼ぶとプロセスが複製される。戻り値が0の場合は子プロセスで、戻り値が0以上の場合は親プロセスの処理となる。二つは同じソースコードで同時進行する。なのでif文などで分岐して親プロセスのソースコードと子プロセスのソースコード二つ書く必要がある.

実装

#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>

#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <syslog.h>

#define MAXFD 64

int demonize(int chdir_flg){

    int i = 0;
    int fd = 0;
    pid_t pid = 0;


    //------------------------------------------------------
    // 状態.
    // プログラム起動時は親プロセス(shell)があり、TTYを持っている.
    //------------------------------------------------------


    //------------------------------------------------------
    // 子プロセスを作成して親を切り離しシェルに制御を戻す.
    //------------------------------------------------------
    if((pid = fork()) == -1){

        return -1;

    // 子プロセスを作成したら親プロセスは終了する.
    }else if(pid != 0){

        // ユーザー定義のクリーンアップ関数を呼ばないように_exit()を行う.
        _exit(0);
    }


    //------------------------------------------------------
    // 状態.
    // 子プロセスのみとなったがTTYは保持したまま.
    //------------------------------------------------------


    //------------------------------------------------------
    // TTYを切り離してセッションリーダー化、プロセスグループリーダー化する.
    //------------------------------------------------------
    setsid();


    //------------------------------------------------------
    // HUPシグナルを無視.
    // 親が死んだ時にHUPシグナルが子にも送られる可能性があるため.
    //------------------------------------------------------
    signal(SIGHUP, SIG_IGN);


    //------------------------------------------------------
    // 状態.
    // このままだとセッションリーダーなのでTTYをオープンするとそのTTYが関連づけられてしまう.
    //------------------------------------------------------


    //------------------------------------------------------
    // 親プロセス(セッショングループリーダー)を切り離す.
    // 親を持たず、TTYも持たず、セッションリーダーでもない状態になる.
    //------------------------------------------------------
    if((pid = fork()) == 0){
        // 子プロセス終了.
        _exit(0);
    }



    //------------------------------------------------------
    // デーモンとして動くための準備を行う.
    //------------------------------------------------------

    // カレントディレクトリ変更.
    if(chdir_flg == 0){

        // ルートディレクトリに移動.
        chdir("/");
    }



    // 親から引き継いだ全てのファイルディスクリプタのクローズ.
    for(i = 0; i < MAXFD; i++){
        close(i);
    }

    // stdin,stdout,stderrをdev/nullでオープン.
    // 単にディスクリプタを閉じるだけだとこれらの出力がエラーになるのでdev/nullにリダイレクトする.
    if((fd = open("/dev/null", O_RDWR, 0) != -1)){

        // ファイルディスクリプタの複製.
        // このプロセスの0,1,2をfdが指すdev/nullを指すようにする.
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        if(fd < 2){
            close(fd);
        }
    }

    return (0);

}

int main(int argc, const char * argv[]) {

    char buf[256];
    demonize(1);

    // カレントディレクトリの表示.
    syslog(LOG_USER | LOG_NOTICE, "demon:cwd=%s¥n", getcwd(buf, sizeof(buf)));

    return 0;
}

main()関数でdemonize()を呼び出すだけでデーモン化できる。
デーモン化したプログラム起動するコマンドを「/etc/rc.local」に書けばサーバー起動時にバックグラウンドで起動できる。

参考

『Linuxネットワーク プログラミングバイブル』
Scyphus Draft — デーモンの作り方

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away