man 7 daemon
デーモンの作成方法を詳細に説明します。私の答えは、このマニュアルからの抜粋です。
少なくとも 2 種類のデーモンがあります:
<オール>SysV デーモン
従来の SysV デーモンに関心がある場合は、次の手順を実装する必要があります:
<ブロック引用> <オール>/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()
など、作成されたファイルとディレクトリのアクセス モードを直接制御します。/
) に変更します。 )、デーモンが無意識にマウント ポイントのアンマウントをブロックしないようにするためです。getpid()
によって返されます)。 ) を PID ファイルに変換します (例:/run/foobar.pid
)。 (架空のデーモン「foobar」の場合) デーモンを複数回起動できないようにします。これは競合のない方法で実装する必要があります。これにより、以前に PID ファイルに保存された PID が存在しないか、外部プロセスに属していないことが確認されたときにのみ PID ファイルが更新されます。fork()
の前に作成される名前のないパイプまたは同様の通信チャネルを介して実装できます。 したがって、元のプロセスとデーモン プロセスの両方で利用できます。exit()
に電話する 元のプロセスで。デーモンを呼び出したプロセスは、この exit()
に依存できる必要があります。 後に発生 初期化が完了し、すべての外部通信チャネルが確立され、アクセス可能になります。次の警告に注意してください:
<ブロック引用>
BSD daemon()
関数はすべきではありません サブセットのみを実装するため、使用できます
互換性を提供する必要があるデーモン SysV システムを使用するシステムは、上で指摘したスキームを実装する必要があります。ただし、デバッグを容易にし、systemd を使用してシステムへの統合を簡素化するために、この動作をオプションにしてコマンドライン引数を介して構成可能にすることをお勧めします。
daemon()
に注意してください は POSIX に準拠していません。
新しいスタイルのデーモン
新しいスタイルのデーモンの場合、次の手順をお勧めします:
<ブロック引用> <オール>SIGTERM
受信したら、デーモンをシャットダウンして正常に終了します。SIGHUP
を受信し、該当する場合は構成ファイルをリロードしてください。systemd.service(5)
を参照 詳細については。systemd.exec(5)
を参照 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 デーモン サーバー プログラミング