ソフトウェア

UNIX/Linuxの「デーモン」はこうやって作る

by Jeff Hitchcock

WindowsやAndroidを搭載したデバイスの動作を軽快にする方法を調べると「タスク」や「プロセス」「デーモン」といった言葉が目に入ります。特にデーモンはUNIX/Linuxにおいて、ユーザーとの対話を行うための制御端末を持たないバックグラウンドプロセスとして、ウェブサーバーやメールサーバーの役割を担う存在。そのデーモンの生成方法について、エンジニアのAryaman Sharda氏が説明しています。

Understanding Daemons (in Unix) - Digital Bunker
https://blog.digitalbunker.dev/2020/09/03/understanding-daemons-unix/

デーモンとは、バックグラウンドで動作し特定のイベント発生を待機しているプロセスのこと。代表的なデーモンとしてはウェブサーバーのhttpdや、SSH接続を提供するsshdなどがあります。

UNIX/LinuxはOSブート時、initと呼ばれるプロセスを起動し、「1」をプロセスの識別番号であるPIDとしてinitに割り当てるとSharda氏。initは制御端末を持たないデーモンで、すべてのプロセスはinitによって起動される子プロセスとなります。Linuxで「pstree」コマンドを実行すると、initがすべてのプロセスの頂点に位置していることがよくわかります。


ブートが完了すると、OSはあらかじめ用意されたデーモンを起動していくとのこと。これらのデーモンは子プロセスを生成するfork関数とプロセスを正常終了するexit関数によって生成され、最終的にはinitプロセスを親プロセスに持つプロセスとなります。

また、最近のLinuxディストリビューションはsystemdを採用しているため、すべてのプロセスの親プロセスであるPID 1のプロセスはsystemdとなっているのが一般的です。


新しくデーモンを生成するには、まずfork関数を呼び出して、子プロセスを生成するとのこと。


子プロセスは、親プロセスのCPUレジスタを共有するので、生成された子プロセスと親プロセスは両方ともfork関数後の命令を並列実行するとSharda氏は説明。親プロセスをexit関数で終了すれば、子プロセスは自動的にinitを親プロセスとするデーモンになります。


実際にデーモンを実装する場合は、もう少し複雑な処理が必要とのこと。Sharda氏は以下のサンプルコードをもとに、デーモンの作り方を説明しています。


デーモンを生成するためのcreate_daemon関数内の最初の処理として、変数pidを初期化してfork関数を呼び出し、子プロセスを生成しています。

static void create_daemon()
{
    pid_t pid;

    pid = fork();


fork関数が正しく終了しているかどうかの確認も実行。PIDが正の値であれば子プロセスが正しく生成されたと判定しているとのこと。

if (pid < 0)
    exit(EXIT_FAILURE);

if (pid > 0)
    exit(EXIT_SUCCESS);


続いて、setsid関数で新しいセッションを作成します。セッションはひとつの制御端末に対応するプロセスの集まりで、セッションリーダーとなる親プロセスを終了すると、すべての子プロセスも同じく終了します。ただし、新しいセッションを作成した時点では、セッションは制御端末を持っていません。制御端末を持たないプロセスを生成できたことでデーモン生成は完了したかに思えますが、処理は続きます。

if (setsid() < 0)
    exit(EXIT_FAILURE);


制御端末や子プロセスから送信されるシグナルを無視し、デーモン生成に影響が出ないように処理が行われています。

signal(SIGCHLD, SIG_IGN);

signal(SIGHUP, SIG_IGN);


新しいセッションを作成した後、fork関数をもう一度呼び出し。setsid関数で制御端末を持たないプロセスを生成していますが、このままではプロセスが新規セッションのセッションリーダーであるため、ユーザーが別の端末を開いた場合などに制御端末を取得してしまう可能性があるとのこと。制御端末を持たないデーモンを確実に生成するために、まずsetsid関数で既存のセッションとは別の新しいセッションにプロセスを移動し、さらに制御端末との関連付けを防ぐために、もう一度子プロセスを生成して親プロセスを終了することで、制御端末を取得する可能性をゼロにしているというわけです。

pid = fork();

if (pid < 0)
    exit(EXIT_FAILURE);

if (pid > 0)
    exit(EXIT_SUCCESS);


続いて新しくファイルやディレクトリを作成する際のumaskを設定。UNIX/Linuxではファイルを新しく作成する際の権限は0666(所有者、グループ、その他ともに読み書き可能)、ディレクトリは0666に実行権限を追加した0777で作成されますが、例えばumaskを0022に設定すると、ファイルの権限は0644(所有者は読み書き可能、グループ、その他は読み取りのみ可能)、ディレクトリの権限は0755(所有者は読み書き実行可能、グループ、その他は読み取りと実行のみ可能)で新規作成されるようになります。このコードではumaskを0000に設定しています。

umask(0);


ルートディレクトリに移動して、プロセスが持つすべてのファイルディスクリプタを閉じます。

chdir("/");

int x;
for (x = sysconf(_SC_OPEN_MAX); x>=0; x--)
{
    close (x);
}


後はメイン関数内でcreate_daemon関数を呼び出し、無限ループ内に処理を記述すればデーモンの完成。

int main()
{
    create_daemon();

    while (1)
    {
	何らかの処理
    }

    return EXIT_SUCCESS;
}


ちなみに、デーモンという単語はエントロピー増大則に反する思考実験上の存在「マクスウェルの悪魔(Maxwell's demon)」のように、裏側でこっそり仕事を行うことから名付けられています。

UNIX/Linuxでよく使われる「Daemon」(デーモン)プロセスの語源とは? - GIGAZINE

この記事のタイトルとURLをコピーする

・関連記事
Windowsの「タスクマネージャー」を開発した本人が直々に使い方や知られざる機能を伝授 - GIGAZINE

全能テキストエディタ「Vim」の歴史と開発者に広く普及した理由 - GIGAZINE

「Linux搭載PC」を名刺にしてしまった猛者が登場 - GIGAZINE

無料でiPhoneやiPad上でコマンドやプログラムを実行できるターミナルアプリ「a-shell」 - GIGAZINE

「Goの父」ロブ・パイクの「プログラミング5カ条」、ネット上で話題に - GIGAZINE

おなじみ「ping」コマンドの生みの親が20年以上前に開発秘話を記したブログ - GIGAZINE

in ソフトウェア, Posted by log1n_yi

You can read the machine translated English article here.