GNU/Linux >> Linux の 問題 >  >> Linux

Linux でのデーモンの作成

man 7 daemon デーモンの作成方法を詳細に説明します。私の答えは、このマニュアルからの抜粋です。

少なくとも 2 種類のデーモンがあります:

<オール>
  • 従来の SysV デーモン (古いスタイル)、
  • systemd デーモン (新しいスタイル)。
  • SysV デーモン

    従来の SysV デーモンに関心がある場合は、次の手順を実装する必要があります:

    <ブロック引用> <オール>
  • 標準の input を除く、開いているすべてのファイル記述子を閉じる 、出力 、およびエラー (つまり、最初の 3 つのファイル記述子 0、1、2)。これにより、誤って渡されたファイル記述子がデーモン プロセスに残ることがなくなります。 Linux では、これは /proc/self/fd まで繰り返すことで実装するのが最適です。 、ファイル記述子 3 から getrlimit() によって返される値まで反復するフォールバック RLIMIT_NOFILE の場合 .
  • すべてのシグナル ハンドラをデフォルトにリセットします。これは、使用可能なシグナルを _NSIG の制限まで反復することによって行うのが最適です。 そしてそれらを SIG_DFL にリセットします .
  • sigprocmask() を使用してシグナル マスクをリセットします .
  • 環境ブロックをサニタイズし、デーモンのランタイムに悪影響を及ぼす可能性のある環境変数を削除またはリセットします。
  • fork() に電話する 、バックグラウンド プロセスを作成します。
  • 子で setsid() を呼び出します 任意の端末から切り離して、独立したセッションを作成します。
  • 子で、fork() を呼び出します もう一度、デーモンが端末を再取得できないようにします。
  • exit() に電話する これにより、2 番目の子 (実際のデーモン プロセス) だけが残ります。これにより、すべてのデーモンと同様に、デーモン プロセスの親が init/PID 1 に変更されます。
  • デーモン プロセスで、/dev/null を接続します。 標準の入力へ 、出力 、およびエラー .
  • デーモン プロセスで、umask をリセットします。 ファイルモードが open() に渡されるように、0 に 、 mkdir() など、作成されたファイルとディレクトリのアクセス モードを直接制御します。
  • デーモン プロセスで、現在のディレクトリをルート ディレクトリ (/) に変更します。 )、デーモンが無意識にマウント ポイントのアンマウントをブロックしないようにするためです。
  • デーモン プロセスで、デーモン PID を書き込みます (getpid() によって返されます)。 ) を PID ファイルに変換します (例:/run/foobar.pid)。 (架空のデーモン「foobar」の場合) デーモンを複数回起動できないようにします。これは競合のない方法で実装する必要があります。これにより、以前に PID ファイルに保存された PID が存在しないか、外部プロセスに属していないことが確認されたときにのみ PID ファイルが更新されます。
  • デーモン プロセスでは、可能であれば特権を削除します。
  • デーモン プロセスから、開始された元のプロセスに初期化が完了したことを通知します。これは、最初の fork() の前に作成される名前のないパイプまたは同様の通信チャネルを介して実装できます。 したがって、元のプロセスとデーモン プロセスの両方で利用できます。
  • exit() に電話する 元のプロセスで。デーモンを呼び出したプロセスは、この exit() に依存できる必要があります。 に発生 初期化が完了し、すべての外部通信チャネルが確立され、アクセス可能になります。
  • 次の警告に注意してください:

    <ブロック引用>

    BSD daemon() 関数はすべきではありません サブセットのみを実装するため、使用できます

    互換性を提供する必要があるデーモン SysV システムを使用するシステムは、上で指摘したスキームを実装する必要があります。ただし、デバッグを容易にし、systemd を使用してシステムへの統合を簡素化するために、この動作をオプションにしてコマンドライン引数を介して構成可能にすることをお勧めします。

    daemon() に注意してください は POSIX に準拠していません。

    新しいスタイルのデーモン

    新しいスタイルのデーモンの場合、次の手順をお勧めします:

    <ブロック引用> <オール>
  • If SIGTERM 受信したら、デーモンをシャットダウンして正常に終了します。
  • If SIGHUP を受信し、該当する場合は構成ファイルをリロードしてください。
  • サービスのエラーや問題を検出するために init システムで使用されるため、メイン デーモン プロセスから正しい終了コードを提供します。 SysV init スクリプトの LSB 推奨事項で定義されている終了コード スキームに従うことをお勧めします。
  • 可能であれば、D-Bus IPC システムを介してデーモンの制御インターフェースを公開し、初期化の最後のステップとしてバス名を取得します。
  • systemd に統合するには、デーモンの開始、停止、その他の保守に関する情報を含む .service ユニット ファイルを提供します。 systemd.service(5) を参照 詳細については。
  • ファイル、サービス、およびその他のリソースへのデーモンのアクセスを制限するために、できる限り init システムの機能に依存します。つまり、systemd の場合、独自のリソース制限制御を実装する代わりに、systemd のリソース制限制御に依存します。デーモンにコードを実装するのではなく、コードを削除するsystemdの特権など。 systemd.exec(5) を参照
  • D-Bus を使用する場合は、D-Bus サービス アクティベーション構成ファイルを指定して、デーモンをバス アクティベート可能にします。これには複数の利点があります。デーモンをオンデマンドで遅延起動できます。それを必要とする他のデーモンと並行して開始することができます。これにより、並行化と起動速度が最大化されます。バスはアクティブ化可能なサービスの要求をキューに入れるため、バス要求を失うことなくデーモンを再起動できます。詳細は以下をご覧ください。
  • デーモンがソケットを介して他のローカル プロセスまたはリモート クライアントにサービスを提供する場合は、以下に示すスキームに従って、ソケットをアクティブ化できるようにする必要があります。 D-Bus アクティベーションと同様に、これにより、サービスのオンデマンド開始が可能になるだけでなく、サービス開始の並列化が改善されます。また、ステートレス プロトコル (syslog、DNS など) の場合、ソケットベースのアクティベーションを実装するデーモンは、単一の要求を失うことなく再起動できます。詳細は以下をご覧ください。
  • 該当する場合、デーモンは sd_notify(3) を介して、起動の完了またはステータスの更新について init システムに通知する必要があります。 インターフェース。
  • syslog() を使用する代わりに システム syslog サービスに直接ログを記録するための呼び出し。新しいスタイルのデーモンは、fprintf() を介して単純に標準エラーにログを記録することを選択できます。 、次に init システムによって syslog に転送されます。ログ レベルが必要な場合は、Linux カーネルの printk() レベルシステム。詳細については、sd-daemon(3) を参照してください。 と systemd.exec(5) .
  • 詳細については、man 7 daemon 全体をお読みください。 .


    <ブロック引用>

    Linux では、停止できず、ファイルシステムの変更を監視するデーモンを追加したいと考えています。変更が検出された場合は、開始されたコンソールへのパス + 改行を書き込む必要があります。

    デーモンはバックグラウンドで動作し、(通常は...) TTY に属していません。そのため、標準出力/標準エラー出力をおそらく必要な方法で使用することはできません。通常は syslog デーモン (syslogd) ) は、メッセージをファイル (デバッグ、エラーなど) に記録するために使用されます。

    それに加えて、必要な手順がいくつかあります プロセスをデーモン化します。

    私の記憶が正しければ、これらの手順は次のとおりです。

    • フォーク 親プロセスをオフにして、フォークが成功した場合は終了させます。 -> 親プロセスが終了したため、子プロセスがバックグラウンドで実行されるようになりました。
    • setsid - 新しいセッションを作成します。呼び出しプロセスは、新しいセッションのリーダーになり、新しいプロセス グループのプロセス グループ リーダーになります。プロセスは制御端末 (CTTY) から切り離されました。
    • 信号をキャッチ - 信号を無視または処理する
    • 再びフォーク &親プロセスを終了させて​​、セッションをリードするプロセスを確実に取り除きます。 (セッション リーダーのみが TTY を再度取得できます。)
    • chdir - デーモンの作業ディレクトリを変更します。
    • umask - デーモンの必要に応じてファイル モード マスクを変更します。
    • 閉じる - 親プロセスから継承された可能性のある開いているファイル記述子をすべて閉じます。

    出発点として、基本的な手順を示すこのスケルトン コードを見てください。このコードは、GitHub でフォークすることもできます:Linux デーモンの基本的なスケルトン

    /*
     * daemonize.c
     * This example daemonizes a process, writes a few log messages,
     * sleeps 20 seconds and terminates afterwards.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <syslog.h>
    
    static void skeleton_daemon()
    {
        pid_t pid;
    
        /* Fork off the parent process */
        pid = fork();
    
        /* An error occurred */
        if (pid < 0)
            exit(EXIT_FAILURE);
    
        /* Success: Let the parent terminate */
        if (pid > 0)
            exit(EXIT_SUCCESS);
    
        /* On success: The child process becomes session leader */
        if (setsid() < 0)
            exit(EXIT_FAILURE);
    
        /* Catch, ignore and handle signals */
        //TODO: Implement a working signal handler */
        signal(SIGCHLD, SIG_IGN);
        signal(SIGHUP, SIG_IGN);
    
        /* Fork off for the second time*/
        pid = fork();
    
        /* An error occurred */
        if (pid < 0)
            exit(EXIT_FAILURE);
    
        /* Success: Let the parent terminate */
        if (pid > 0)
            exit(EXIT_SUCCESS);
    
        /* Set new file permissions */
        umask(0);
    
        /* Change the working directory to the root directory */
        /* or another appropriated directory */
        chdir("/");
    
        /* Close all open file descriptors */
        int x;
        for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
        {
            close (x);
        }
    
        /* Open the log file */
        openlog ("firstdaemon", LOG_PID, LOG_DAEMON);
    }
    
    int main()
    {
        skeleton_daemon();
    
        while (1)
        {
            //TODO: Insert daemon code here.
            syslog (LOG_NOTICE, "First daemon started.");
            sleep (20);
            break;
        }
    
        syslog (LOG_NOTICE, "First daemon terminated.");
        closelog();
    
        return EXIT_SUCCESS;
    }
    


    • コードをコンパイルします:gcc -o firstdaemon daemonize.c
    • デーモンを開始します:./firstdaemon
    • すべてが正常に機能しているかどうかを確認してください:ps -xj | grep firstdaemon

    • 出力は次のようになります:

    +------+------+------+------+-----+-------+------+------+------+-----+
    | PPID | PID  | PGID | SID  | TTY | TPGID | STAT | UID  | TIME | CMD |
    +------+------+------+------+-----+-------+------+------+------+-----+
    |    1 | 3387 | 3386 | 3386 | ?   |    -1 | S    | 1000 | 0:00 | ./  |
    +------+------+------+------+-----+-------+------+------+------+-----+
    

    ここに表示される内容:

    • デーモンには制御端末がありません (TTY =? )
    • 親プロセス ID (PPID ) は 1 です (初期化プロセス)
    • PID !=SID これは、私たちのプロセスがセッションリーダーではないことを意味します
      (2 番目の fork() のため)
    • PID !=SID のため、プロセスは TTY を再び制御できません

    syslog の読み取り:

    • syslog ファイルを見つけます。私のはここにあります:/var/log/syslog
    • 実行:grep firstdaemon /var/log/syslog

    • 出力は次のようになります:

      firstdaemon[3387]: First daemon started.
      firstdaemon[3387]: First daemon terminated.
    


    注: 実際には、シグナル ハンドラーを実装し、ログを適切に設定する必要もあります (ファイル、ログ レベルなど)。

    参考資料:

    • Linux-UNIX-Programmierung - ドイツ語
    • Unix デーモン サーバー プログラミング

    Linux
    1. Linuxにvtopをインストールする方法

    2. Linux スレッドの紹介 – パート I

    3. Linux での kill コマンドの例

    1. Linux の CreateProcess?

    2. Linux:サービスへの処理

    3. シェルでプロセスをデーモン化しますか?

    1. Linuxでゾンビプロセスを強制終了する方法

    2. Linux –プロセスのネットワークアクセスをブロックしますか?

    3. Linux –「サブリーパー」プロセス?