なぜfork()はファイル記述子を返すように設計されているのですか?


16

彼の上に関するウェブページの自己パイプのトリック、ダン・バーンスタインはと競合状態を説明select()し、信号、回避策を提供していますし、その結論を

もちろん、正しいことはfork()プロセスIDではなくファイル記述子を返すことです。

彼はこれによって何を意味しselect()ますか?シグナルハンドラを使用してそれらの状態変化を通知する代わりに、子プロセスで状態変化を処理することができるということですか?


その記事は入力と出力を混同しますか、それとも正しく読みませんか?
ctrl-alt-delor

パイプを介して配信される信号を要求できます。それが私がしていることです。
ctrl-alt-delor

@ ctrl-alt-delor、ええ、彼は少し奇妙に「パイプ入出力」を使用しているようですが、彼が書いている場所とパイプから読んでいる場所は明らかだと思います。そのテキストは2003年signalfdのもので、そのようなものは当時のものであったかどうかはわかりませんか?
ilkkachu

5
ダンは、彼が何を話しているかを知っていますが、彼は少し故意に挑発的かもしれません。私が故意に挑発的であったなら、私はもちろん、正しいことはSIGCHLDを取り除くことであると意見を述べました。
スティーブサミット

1
@mosvy少し誇張していますが、SIGCHLDを使おうとしたすべてのプログラムとすべてのプログラマーが問題を抱えています。それは起こるのを待っている競合状態です。私たちがブロックしていたときwait()、あなたができないことがあったので、誰かがSIGCHLDを発明しましたが、それは悪い仕事でした。私の経験では、今、彼らはノンブロッキング、素敵な散水、存在することwait3()wait4()および/またはwaitpid()キーの場所での通話(おそらくメインイベントループ)は、非常に優れた代替手段です。
スティーブサミット

回答:


14

問題はソースに記載されており、select()などの信号によって中断されるはずですSIGCHLDが、場合によってはうまく機能しません。そのため、回避策は、パイプにシグナルを書き込むことselect()です。ファイル記述子を監視することselect()が目的であるため、問題を回避できます。

回避策は、基本的に信号イベントをファイル記述子イベントに変換します。fork()そもそもfdを返しただけであれば、そのfdはおそらくで直接使用できるため、回避策は必要ありませんselect()

そう、最後の段落の説明は私には正しいようです。


fd(または他の種類のカーネルハンドル)が単純なプロセスID番号よりも優れているもう1つの理由は、プロセスが終了した後にPIDを再利用できるからです。シグナルをプロセスに送信する場合、これは問題になることがあります。プロセスがあなたが考えているものであり、同じPIDを再利用している別のものではないことを確実に知ることができない場合があります。(親がwait()PIDを解放するために子で実行する必要があるため、子プロセスにシグナルを送信する場合、これは問題ではないと思いますが。)


とはいえ、PIDの再利用が問題であることについて読んだケースを正確に思い出せないので、それについて詳しく説明したり明確にしたり、上記を編集したい場合は、お気軽にそうしてください。
ilkkachu

2
既に述べたように、親が自身の子pidが再利用されていることを見つける言い訳はありません。それが電話するものであるので、それはその状況を完全にコントロールしていますwait()
ジョシュア

これらはゾンビプロセスと呼ばれます:「実行は完了したが、プロセステーブルにまだエントリがあるプロセス:「終了状態」のプロセスです。これは、親を許可するためにエントリが必要な子プロセスに対して発生します。子の終了ステータスを読み取るプロセス」
ラッシー

6
Linux からファイル記述子(pidfd)を返すことができるようになりましたclone。これは、フォークがLInuxで呼び出す実際のシステムコールです。これを有効にするフラグは呼び出されますCLONE_PIDFD-たとえば、lwn.net / Articles / 784831を参照してください。
KJツァナクティディス

1
@Lie Ryan、Re " fork()を使用すると、ローカルハンドルではなくグローバルハンドルが返されます。概念的には、Windowsはプロセスハンドルを使用するためより正確です。pidと終了コードは、プロセスのすべてのハンドルが閉じられるまで(親が刈り取るのを待つのではなく)固定され、UNIXシステムで一般的な競合状態を回避します。ハンドルがプロセスを存続させる場合、グローバルハンドルではなくローカルハンドルであることがはるかに理にかなっています。
池上

9

これは、「Unixの設計がそれとは異なる場合は素晴らしいことです」という行の単なる考察です。

PIDの問題は、それらが別のプロセスで再利用できるグローバルな名前空間に存在することであり、常に子プロセスfork()を参照することが保証される何らかのハンドルを親に返すと便利です。継承またはUNIXソケット/ SCM_RIGHTS[1]を介して他のプロセスに渡すことができます。

Linuxでそれを「修正」するための最近の取り組みについては、PIDではなくpid-fdを返すフラグを追加するなど、ここの説明も参照してくださいclone()

しかし、それでも、親ループに子プロセスの状態を通知する信号だけがメインループで処理したいものではないため、セルフパイプハック[2]またはより優れたインターフェイスの必要性を排除しません。プログラムの。残念ながら、epoll(7) + signalfd(2)Linuxやkqueue(2)BSDのようなものは標準ではありません。唯一の標準インターフェース(ただし、古いシステムではサポートされていません)は、はるかに劣っていpselect(2)ます。

[1] waitpid()syscallが返され、その戻り値が使用されたときまでにPIDがリサイクルされないようにすることは、waitid(.., WNOWAIT)代わりに使用することにより、おそらく新しいシステムで達成できます。

[2]彼がそれを発明したというDJ Bernsteinの主張についてはコメントしません(アポファシスはごめんなさい;-))。


8

Bernsteinはこの「正しいこと」の発言についてあまり文脈を示していませんが、推測を危険にさらします:fork(2)がPIDを返すことは、open(2)、creat(2)などがファイル記述子を返すことと矛盾します。Unixシステムの残りの部分は、PIDの代わりにプロセスを表すファイル記述子を使用してプロセス操作を行うことができます。システムコールsignalfd(2)が存在します。これにより、シグナルとファイル記述子の間の相互作用が多少改善され、プロセスを表すファイル記述子が機能することが示されます。


signalfd(2)は素晴らしく見えます。言及してくれてありがとう!残念なことに、Linux専用です。
ラッシー

1
pidfd_openLinux についても議論されています。たとえば、lwn.net
Articles /
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.