デーモンを作成するときにダブルフォークを実行する理由は何ですか?


165

私はpythonでデーモンを作成しようとしています。私が現在フォローしているいくつかの良いリソースを持っている次の質問を見つけましたが、なぜダブルフォークが必要なのか知りたいです。私はグーグルをひっくり返しました、そしてそれが必要であると宣言する多くのリソースを見つけました、しかし理由はではありません。

デーモンが制御端末を取得できないようにするためだと言う人もいます。2番目のフォークなしでこれをどのように実行しますか?反響とは何ですか?



2
ダブルフォークを実行する際の1つの問題は、親が孫プロセスのPIDを簡単に取得できないことです(fork()呼び出しは子のPIDを親に返すため、子プロセスのPIDを取得するのは簡単ですが、簡単ではありません)プロセスのPIDを取得します)。
Craig McQueen

回答:


105

質問で参照されているコードを見ると、正当化は次のとおりです。

2人目の子供をフォークし、ゾンビを防ぐためにすぐに終了します。これにより、2番目の子プロセスが孤立し、initプロセスがそのクリーンアップを担当します。また、最初の子は制御端末を持たないセッションリーダーであるため、将来端末を開くことで取得できます(System Vベースのシステム)。この2番目のフォークは、子がセッションリーダーではないことを保証し、デーモンが制御端末を取得するのを防ぎます。

したがって、デーモンがinitに親を再設定し(デーモンを開始するプロセスが長命である場合に備えて)、確実にデーモンが制御ttyを再取得する可能性を排除します。したがって、これらのどちらのケースにも当てはまらない場合は、1つのフォークで十分です。「Unixネットワークプログラミング-Stevens」には、これに関する優れたセクションがあります。


28
これは完全に正しいわけではありません。デーモンを作成する標準的な方法は、単に行うことですp=fork(); if(p) exit(); setsid()。この場合、親も終了し、最初の子プロセスの親が変更されます。ダブルフォークの魔法は、デーモンがttyを取得するのを防ぐためにのみ必要です。
parasietje

1
したがって、私が理解しているように、私のプログラムが起動しforkschildプロセスが開始されると、この最初の子プロセスがになりsession leader、TTY端末を開くことができます。しかし、この子から再度フォークして、この最初の子を終了すると、2番目にフォークされた子はにならずsession leader、TTY端末を開くことができません。このステートメントは正しいですか?
tonix

2
@tonix:単にフォークしてもセッションリーダーは作成されません。それはによって行われsetsid()ます。したがって、最初にフォークされたプロセスは呼び出し後にセッションリーダーになり、setsid()その後再びフォークして、最後の二重フォークプロセスがセッションリーダーではなくなります。setsid()セッションリーダーであるという要件以外に、あなたはその場にいます。
dbmikus

169

私はダブルフォークを理解しようとしていて、この質問をここで偶然見つけました。多くの研究の後、これは私が考え出したものです。うまくいけば、それは同じ質問を持つ誰にとっても物事をより明確にするのに役立つでしょう。

Unixでは、すべてのプロセスがグループに属し、グループはセッションに属しています。これが階層です…

セッション(SID)→プロセスグループ(PGID)→プロセス(PID)

プロセスグループの最初のプロセスがプロセスグループリーダーになり、セッションの最初のプロセスがセッションリーダーになります。すべてのセッションに1つのTTYを関連付けることができます。セッションリーダーだけがTTYを制御できます。プロセスが本当にデーモン化される(バックグラウンドで実行される)には、セッションリーダーが強制終了され、セッションがTTYを制御する可能性がないようにする必要があります。

私は、UbuntuでこのサイトからSander MarechalのPythonサンプルデーモンプログラムを実行しました。これが私のコメント付きの結果です。

1. `Parent`    = PID: 28084, PGID: 28084, SID: 28046
2. `Fork#1`    = PID: 28085, PGID: 28084, SID: 28046
3. `Decouple#1`= PID: 28085, PGID: 28085, SID: 28085
4. `Fork#2`    = PID: 28086, PGID: 28085, SID: 28085

プロセスがなDecouple#1ので、後にセッションリーダーになることに注意してくださいPID = SID。それでもTTYを制御できます。

Fork#2はセッションリーダーではなくなりましたPID != SID。このプロセスがTTYを制御することはありません。本当にデーモン化。

個人的に、2倍の用語で混乱していると思います。より良いイディオムはfork-decouple-forkかもしれません。

興味のある追加リンク:


また、2回フォークすると、親プロセスが長時間実行されたときにゾンビが作成されなくなり、なんらかの理由でそのプロセスが終了したことを知らせるシグナルのデフォルトハンドラーが削除されます。
Trismegistos 2014年

ただし、2番目はdecoupleを呼び出してセッションリーダーになり、端末を取得することもできます。
Trismegistos 2014

2
本当じゃない。最初のfork()方法では、親を閉じた場合、ゾンビの作成をすでに防止しています。
parasietje 2015年

1
上記の引用された結果を生成する最小の例:gist.github.com/cannium/7aa58f13c834920bb32c
できます。

1
シングルのsetsid() に電話するのは良いことfork()でしょうか?実際、私はこの質問の答えがそれに答えると思います。
Craig McQueen

118

厳密に言えば、ダブルフォークはデーモンをの子として再ペアレント化することとは何の関係もありませんinit。子を再親化するために必要なことは、親が終了する必要があることだけです。これは、単一のフォークだけで実行できます。また、それだけでダブルフォークを実行しても、デーモンプロセスの親はに変更されませんinit。デーモンの親終了する必要があります。つまり、適切なデーモンをフォークすると、親は常に終了し、デーモンプロセスの親がに変更されinitます。

では、なぜダブルフォークなのでしょうか。POSIX.1-2008セクション11.1.3「Controlling Terminal」には答えがあります(強調を追加):

セッションの制御端末は、実装で定義された方法でセッションリーダーによって割り当てられます。セッションリーダーに制御端末がなく、O_NOCTTYオプションを使用せずにセッションに関連付けられていない端末デバイスファイルを開く場合(を参照open())、端末がセッションリーダーの制御端末になるかどうかは実装定義です。セッションリーダーではないプロセスが端末ファイルを開いたO_NOCTTY場合、またはオプションがで使用されたopen()場合、その端末は呼び出しプロセスの制御端末にはなりません

これは、デーモンプロセスがこのようなことをする場合...

int fd = open("/dev/console", O_RDWR);

...次に、デーモンプロセス/dev/consoleセッションリーダーであるかどうか、およびシステムの実装に応じて、デーモンプロセスが制御端末として取得する場合があります。プログラムは、プログラムが最初にセッションリーダーでないことを確認すると、上記の呼び出しが制御端末を取得しないことを保証できます。

通常、デーモンを起動すると、setsidが呼び出され(を呼び出した後の子プロセスからfork)、デーモンをその制御端末から切り離します。ただし、呼び出しsetsidは、呼び出しプロセスが新しいセッションのセッションリーダーになることも意味します。これにより、デーモンが制御端末を再取得できる可能性が残ります。ダブルフォーク技術は、デーモンプロセスがセッションリーダーではないことを保証します。これopenにより、上記の例のようにへの呼び出しが、デーモンプロセスが制御端末を再取得することはありません。

ダブルフォークのテクニックは少し偏執的です。デーモンが端末デバイスファイルを開かないことがわかっている場合は、必要ない場合があります。また、一部のシステムでは、動作が実装定義であるため、デーモンが端末デバイスファイルを開いたとしても、その必要がない場合があります。ただし、実装で定義されていないことの1つは、セッションリーダーだけが制御端末を割り当てることができることです。プロセスがセッションリーダーでない場合は、制御端末を割り当てることができません。したがって、偏執的になり、実装定義の詳細に関係なく、デーモンプロセスが制御端末を誤って取得できないようにしたい場合は、ダブルフォークテクニックが不可欠です。


3
+1残念ながら、この回答は質問が行われてから4年後まで来ました。
Tim Seguine 2013

12
しかし、それでもデーモンが制御端末を再取得できないことが非常に重要である理由は説明されていません
UloPe

7
キーワードは、「うっかり」管理端末を取得することです。プロセスがたまたまターミナルを開いて、それがプロセス制御ターミナルになった場合、誰かがそのターミナルから^ Cを発行した場合よりも、プロセスが終了する可能性があります。そのため、不注意でプロセスが発生しないようにプロセスを保護するとよいでしょう。個人的には、ターミナルを開かないことがわかっているコードについては、単一のフォークとsetsid()を使用します。
BobDoolittle 2014

1
@BobDoolittleどうやってこれが「うっかり」起こりますか?プロセスは、そうするように書かれていない場合、端末を開くだけではありません。プログラマがコードを知らず、ttyを開くかどうかもわからない場合は、ダブルフォークが役立つでしょう。
マリウス

10
@Mariusデーモンの構成ファイルに次のような行を追加するとどうなるか想像してみてくださいLogFile=/dev/console。プログラムは、開くファイルを常にコンパイル時に制御できるわけではありません;)
Dan Moulding

11

Bad CTKから取得

「Unixの種類によっては、デーモンモードに移行するために、起動時にダブルフォークを実行する必要があります。これは、シングルフォークが制御端末から切り離されることが保証されていないためです。」


3
シングルフォークで制御端末から切り離さずに、ダブルフォークで切り離すにはどうすればよいですか?これはどのUNIXで発生しますか?
bdonlan 2009年

12
デーモンはその入出力ファイル記述子(fds)を閉じる必要があります。そうしないと、デーモンは起動された端末に接続されたままになります。フォークされたプロセスは、親から継承します。どうやら、最初の子はfdsを閉じますが、それですべてがクリーンアップされるわけではありません。2番目のフォークにはfdsが存在しないため、2番目の子は何にも接続できません。
アーロンディグラ2009年

4
@Aaron:いいえ、デーモンsetsidは最初のフォークの後で呼び出すことにより、制御ターミナルから適切に「切り離し」ます。次に、再びforkし、セッションリーダー(を呼び出したプロセス)を終了させることで、制御端末から切り離されたままになるようにしますsetsid
Dan Molding

2
@bdonlan:fork制御端末から切り離されるわけではありません。それsetsidだけです。ただしsetsid、プロセスグループリーダーから呼び出された場合は失敗します。したがって、プロセスグループリーダーではないプロセスから呼び出されるようforksetsidするために、イニシャルを前に実行する必要がありますsetsid。2番目forkは、最終プロセス(デーモンになるプロセス)がセッションリーダーではないことを確認します。制御端末を取得できるのはセッションリーダーだけなので、この2番目のフォークは、デーモンが制御端末を誤って再取得しないことを保証します。これは、どのPOSIX OSにも当てはまります。
Dan Molding、2013年

@DanMouldingこれは、2番目の子がsetidを呼び出してセッションリーダーになり、制御端末を取得できるため、制御端末を取得しないことを保証しません。
Trismegistos 2014

7

StephensとRagoによる「Unix環境での高度なプログラミング」によると、2番目のフォークはより推奨されており、デーモンがSystem Vベースのシステムで制御端末を取得しないことを保証するために行われます。


3

1つの理由は、親プロセスがすぐに子のwait_pid()を実行し、その後それを忘れることがあるためです。その後、孫が死ぬと、その親はinitであり、それを待機()し、ゾンビ状態からそれを取り出します。

その結果、親プロセスはforkされた子を認識する必要がなくなり、libなどから長時間実行されているプロセスをforkすることも可能になります。


2

daemon()呼び出しは、成功すると親呼び出し_exit()を持ちます。元々の動機は、子供がデーモン化している間、親が追加の作業を行えるようにすることでした。

これは、デーモンに親プロセスがなく、initに親が変更されていることを確認するために必要であるという誤った考えに基づいている可能性もあります。ただし、これは、親がシングルフォークの場合に終了すると発生します。

つまり、結局のところ、すべてが伝統に帰着するものだと思います。親がいずれにせよ短期間で死ぬ限り、1つのフォークで十分です。



-1

この方法で理解する方が簡単かもしれません:

  • 最初のforkとsetsidは新しいセッションを作成します(ただし、プロセスID ==セッションID)。
  • 2番目のフォークは、プロセスID!=セッションIDを確認します。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.