web-dev-qa-db-ja.com

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

彼の Webページについてセルフパイプトリック)で、Dan Bernsteinはselect()で競合状態を説明していますおよび信号、回避策を提供し、それを結論付けます

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

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

16
Lassi

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

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

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


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

14
ilkkachu

「Unixの設計がこれとは異なるように設計されていればすばらしい」という言葉を単に熟考するだけです。

PIDの問題は、別のプロセスに再利用できるグローバル名前空間に存在することであり、fork()が常に参照することが保証されているある種のハンドルで親に返された場合、それはいいことです。子プロセス。継承またはUNIXソケット/ _SCM_RIGHTSclone()に追加することが含まれます。 。

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

[1] waitpid() syscallが返され、その戻り値が使用されるまでにPIDが再循環されるのを防ぐには、代わりにwaitid(.., WNOWAIT)を使用することにより、新しいシステムでおそらく達成できます。

[2] D.J.についてはコメントしません。バーンスタインは、彼がそれを発明したと主張している(アポファシスについてすみません;-)).

9
mosvy

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

8
Bruce Ediger

重要なのは、 select(2) / poll(2) / を使用してファイル記述子の監視に基づいてイベントループスタイルモデルを操作するプログラムが多数あることです。 epoll(7) 。しかし、歴史的にはファイル記述子を使用して通知しなかったさまざまなイベントがありました-それらの中に、子プロセスのプロセス状態遷移(たとえば、終了)があります。これらの他のイベント(タイマーの期限切れ、シグナル、セマフォの変更などの同期イベントを含む)を個別に処理する必要があるため、イベントループモデルのプログラミングが複雑になります。

過去数年間、Linuxの開発はこの問題を回避してきました。そのため、現在では signalfd(2) (ファイル記述子から信号を読み取り可能にする)、 eventfd(2) (ハンドルがファイル記述子である同期プリミティブ)、および timerfd_create(2) (ファイル記述子を介して通知するタイマーを作成)、これらすべてが、 select(2) / poll(2) / epoll(7)

そして最後に、最近のバージョンのLinuxでは、プロセスのハンドルの概念がファイル記述子として追加されています。 CLONE_PIDFDフラグ clone3() を使用して、ハンドルとしてのファイル記述子が返される子プロセスを作成できます。同様に、ファイル記述子を select(2) / poll(2) / epoll(7) に送ることができ、子プロセスは終了します。

1
mtk