現代のオペレーティングシステムにおける fork() と exec() の先へ
従来の Unix プロセス作成モデルは、fork() と exec() の組み合わせに基づいています。しかし、開発者やシステムアーキテクトの間で、これが現代のコンピューティングにとって非効率な抽象化であるという議論が再燃しています。このモデルは初期の Unix 設計の礎石として機能してきましたが、新しい実行ファイルを置き換える前に親プロセスを複製する必要があるという要件は、多くの場合不要であり、計算コストも高くなります。
fork() + exec() モデルの非効率性
fork() + exec() パターンの核心的な問題は、クローン作成と破棄という冗長なサイクルを生み出してしまうことです。ほとんどのユースケースにおいて、開発者は現在のプロセスのクローンではなく、完全に新しいプロセスを開始したいと考えています。fork() を使用すると、オペレーティングシステムはプロセス状態を複製することを強制されますが、その直後に exec() がその状態を破棄して新しいバイナリをロードします。
パフォーマンス・オーバーヘッドと Copy-on-Write の誤解
Copy-on-Write (CoW) は、すべての物理メモリの即時コピーを防ぐ最適化手法ですが、fork() のパフォーマンス・コストを排除するものではありません。
"It is a weirdly common misconception that that fork() is cheap... it is O(N) on the size of the process, and it always has been. Yes, it's copy on write... but there is a linear relationship between the size of the process and the number of page table entries required to represent it."
カーネルはプロセスのメモリ空間を表すためにページテーブルを複製する必要があるため、fork() のコストはプロセスのサイズに比例して線形に増加します。大規模なプロセスの場合、このオーバーヘッドは、特にプロセスが直後に exec() 呼び出しに続く場合に、重大なボトルネックとなります。
プロセス・クローニングのアーキテクチャ上の批判
パフォーマンス以外にも、fork() モデルは概念的に欠陥のある抽象化であるとして批判されています。主な目的が単に実行ファイルを起動することであるにもかかわらず、クローニング用に設計されたメカニズムの使用を開発者に強いています。
「クローンして修正する」アンチパターン
fork() は親プロセスの正確な複製を作成するため、開発者は fork() の後、exec() の前という、子プロセスを「修正」しなければならない状況に陥ることがよくあります。これには、不要なファイル記述子を閉じたり、環境変数を調整したりすることが含まれます。この「クローンしてから後で修正する」というアプローチは、バグが発生しやすく、直接的なプロセス作成呼び出しよりも直感的ではありません。
他の OS モデルとの比較
一部の開発者は、他のオペレーティングシステムがこれをよりクリーンに実装していると主張しています。例えば、Windows の CreateProcessW インターフェースは、親プロセスの事前のクローンを必要とせずに、開発者が新しいプロセスのパラメータを直接指定できるため、より自然なアプローチとして挙げられます。
fork() + exec() モデルを支持する議論
批判はあるものの、一部の人々は、fork() + exec() モデルのエレガンスは、その柔軟性にありますと主張しています。プロセス作成を2つのステップに分けることで、OS は、新しいプログラムが実行を開始する前に、子プロセスが標準入出力の転送などの標準 API を使用して環境を構成できる時間的猶予を提供します。
結合された呼び出しの課題
代替案の批判者は、spawn や posix_spawn スタイルの結合された呼び出しを行う場合、fork() モデルの柔軟性に匹敵するために、膨大な数の構成パラメータのリストが必要になると示唆しています。これがないと、結合された呼び出しは、制限が厳しすぎるか、あるいは新しい要件が登場するたびに、複雑でメンテナンス不能なパラメータの山となるでしょう。
提案されている代替案と今後の方向性
fork() の置き換えに関する議論では、ロジックをユーザースペースに移動させることや、カーネルレベルのフックを利用することなど、いくつかの道筋が示唆されています。
ユーザースペース・ライブラリと eBPF
一部の提案では、プロセスの繰り返し的な生成(例えば、長時間実行される操作の中で git を繰り返し呼び出すなど)のオーバーヘッドを、ツールを直接リンクできるライブラリに変換することで解決すべきだと述べています。これにより、プロセス作成の必要性を完全に回避できます。
また、別の提案では、より現代的なカーネル・アプローチが示唆されています。おそらく eBPF (extended Berkeley Packet Filter) を利用して、高度なユーザーがフックを介してプロセス作成フローをカスタマイズムできる仕組みを提供し、複雑な構成に必要な柔軟性を維持しつつ、不要なクローニング・ステップをスキップできるようにすることです。