回答:
簡単な答えは、fork
当時の既存のシステムに簡単に適合することができ、バークレーの前身システムがフォークの概念を使用していたためです。
以下からのUnixタイムシェアリングシステムの進化(該当するテキストがされた強調表示):
現代的な形のプロセス制御は、数日で設計および実装されました。既存のシステムにどれほど簡単に適合したかは驚くべきことです。同時に、デザインのわずかに珍しい機能のいくつかが、存在するものに対する小さな、簡単にコード化された変更を表しているため、正確に存在することを簡単に確認できます。良い例は、fork関数とexec関数の分離です。新しいプロセスを作成するための最も一般的なモデルには、実行するプロセスのプログラムを指定することが含まれます。Unixでは、分岐されたプロセスは、明示的なexecを実行するまで親と同じプログラムを実行し続けます。機能の分離は確かにUnixに固有のものではなく、実際にはトンプソンによく知られているバークレーのタイムシェアリングシステムに存在していました。。それでも、主に他の多くを変更せずにforkを簡単に実装できるため、Unixに存在すると想定するのは理にかなっているようです。システムはすでに複数(2つ)のプロセスを処理しました。プロセステーブルがあり、メインメモリとディスクの間でプロセスが交換されました。forkの初期実装のみが必要
1)プロセステーブルの拡張
2)既存のスワップIOプリミティブを使用して、現在のプロセスをディスクスワップ領域にコピーし、プロセステーブルにいくつかの調整を行うフォークコールの追加。
実際、PDP-7のフォークコールには、正確に27行のアセンブリコードが必要でした。もちろん、オペレーティングシステムとユーザープログラムには他の変更が必要であり、それらの一部はかなり興味深く、予期しないものでした。ただし、fork-execの組み合わせは、exec自体が存在しないという理由だけで、かなり複雑になります。その機能は、シェルによって明示的なIOを使用して既に実行されています。
その論文以来、Unixは進化してきました。fork
続いてexec
は、プログラムを実行する唯一の方法ではありません。
vforkは、新しいプロセスがforkの直後にexecを実行する場合に、より効率的なforkとして作成されました。vforkを実行すると、親プロセスと子プロセスは同じデータスペースを共有し、子プロセスがプログラムを実行するか終了するまで、親プロセスは中断されます。
posix_spawnは、新しいプロセスを作成し、単一のシステムコールでファイルを実行します。それは、呼び出し元の開いているファイルを選択的に共有し、そのシグナルの性質と他の属性を新しいプロセスにコピーできるようにするパラメーターの束を取ります。
posix_spawn()
簡単に使用できfork()
、インラインコードと同じフォーク後の再配管ジョブを実行するために使用する必要があるマチネーション(データ構造セットアップ)は、使用fork()
がはるかに簡単であるという説得力のある議論になります。
新しいプロセスをゼロから作成するコマンドを用意しないのはなぜですか? すぐに交換するだけのものをコピーするのは不合理で非効率ではありませんか?
実際、いくつかの理由でおそらくそれほど効率的ではありません。
によって生成される「コピー」fork()
は少し抽象化されています。これは、カーネルがコピーオンライトシステムを使用しているためです。実際に作成する必要があるのは、仮想メモリマップだけです。コピーがすぐにを呼び出すexec()
場合、プロセスのアクティビティによって変更された場合にコピーされるはずだったデータのほとんどは、プロセスがその使用を必要としないため、実際にコピー/作成する必要はありません。
子プロセスのさまざまな重要な側面(たとえば、その環境)は、コンテキストなどの複雑な分析に基づいて個別に複製または設定する必要はありません。それらは、呼び出し元プロセスのそれと同じであると見なされます。これは私たちがよく知っているかなり直感的なシステムです。
#1をもう少し説明すると、「コピーされた」がその後アクセスされないメモリは、少なくともほとんどの場合、実際にはコピーされません。この文脈での例外がありますあなたはプロセスをフォークする場合は子供がで自身を置き換える前に、その後、親プロセスの終了を持っていましたexec()
。私は言うかもしれないが(OSの実装に依存するであろうもの)、十分な空きメモリがある場合、親の多くがキャッシュされる可能性があるため、私はこれが悪用されるだろうどの程度までわかりません。
もちろん、それは表面上は空白のスレートを使用するよりもコピーを使用する方が効率的ではありません。ただし、「空白のスレート」は文字通り何もないので、割り当てが必要です。システムは、同じ方法でコピーする汎用のブランク/新しいプロセステンプレートを持つことができます1が、コピーオンライトフォークと比較すると、実際には何も保存されません。したがって、#1は、「新しい」空のプロセスを使用しても効率が良くないことを示しています。
ポイント2は、フォークを使用する方が効率的である理由を説明しています。子の環境は、完全に異なる実行可能ファイルであっても、親から継承されます。たとえば、親プロセスがシェルであり、子プロセスがWebブラウザーで$HOME
ある場合、どちらも同じですが、どちらかが後でそれを変更できるため、これらは2つの別々のコピーでなければなりません。子供のものはオリジナルで製作されfork()
ます。
1.文字通り意味をなさないかもしれない戦略ですが、私のポイントは、プロセスを作成することは、そのイメージをディスクからメモリにコピーするだけではありません。
fork()
(GLが述べたように、27行のアセンブリのオーダーで)非常に迅速にできます。別の方向を見ると、「ゼロからプロセスを作成」する場合fork()
、作成された空白のプロセスから開始するよりもほんの少しだけ費用がかかります(27行のアセンブリ+ファイルハンドルを閉じるコスト)。したがってfork
、forkとcreateの両方を適切にcreate
処理しますが、createのみを処理できます。
fork
実際にすべてのプロセスメモリをコピーし、非常に高価でした。
Unixにfork
新しいプロセスを作成する機能しかなかった理由は、Unixの哲学の結果だと思います
彼らは一つのことをうまく行う一つの機能を構築します。子プロセスを作成します。
新しいプロセスで何をするかはプログラマー次第です。exec*
関数の1つを使用して別のプログラムを開始するか、execを使用して同じプログラムの2つのインスタンスを使用することはできません。これは便利です。
使用できるので、より大きな自由度が得られます
さらに、1970年代にはやらなければならなかっfork
たexec*
関数呼び出しと関数呼び出しを覚えるだけで済みます。
プロセス作成には2つの哲学があります。継承を伴うフォークと引数を伴う作成です。Unixは明らかにforkを使用します。(たとえば、OSE、およびVMSはcreateメソッドを使用します。)Unixには多くの継承可能な特性があり、定期的に追加されます。継承により、これらの新しい特性は既存のプログラムを変更することなく追加できます!引数付きの作成モデルを使用して、新しい特性を追加すると、作成引数に新しい引数が追加されます。Unixモデルはより単純です。
また、プロセスがそれ自体を複数の部分に分割できる、非常に便利なfork-exec-execモデルも提供します。これは非同期I / Oの形式が存在しない場合に不可欠であり、システム内の複数のCPUを利用する場合に役立ちます。(プリスレッド。)私はこれを何年も、最近でもずっとやってきました。本質的には、複数の「プログラム」を単一のプログラムにコンテナー化することができるため、破損やバージョンの不一致などの余地はまったくありません。
また、fork / execモデルは、特定の子がforkとexecの間に設定された根本的に奇妙な環境を継承する能力を提供します。特に、継承されたファイル記述子のようなもの。(stdio fdの拡張。)createモデルは、create呼び出しの作成者が想定していなかったものを継承する機能を提供しません。
一部のシステムは、ネイティブコードの動的コンパイルもサポートできます。この場合、プロセスは実際に独自のネイティブコードプログラムを作成します。言い換えれば、ソースコード/コンパイラ/リンカーのサイクルを経ることなく、ディスクスペースを占有することなく、自身をその場で書き込む新しいプログラムが必要です。(これを行うVerilog言語システムがあると思います。)フォークモデルはこれをサポートしますが、モデルの作成は通常サポートしません。
fork()関数は、fatherプロセスをコピーするだけでなく、プロセスがfatherまたはsonプロセスであることを示す値を返します。以下の画像は、fork()を父親として使用し、息子:
プロセスが父親の場合に示されているようにfork()は息子のプロセスIDをPID
返します。0
たとえば、リクエストを受信するプロセス(Webサーバー)があり、各リクエストでson process
このリクエストを処理するために作成する場合、これを利用できます。ここでは、父親とその息子は異なるジョブを持っています。
そのため、プロセスのコピーを実行することは、fork()ほど正確なことではありません。
fork
あなたが欲しいと仮定した場合fork
、最初の場所で
I / Oリダイレクトは、forkの後、execの前に最も簡単に実装されます。子は、子であることを認識して、ファイル記述子を閉じたり、新しい記述子を開いたり、dup()またはdup2()して、親に影響を与えることなく正しいfd番号などを取得できます。それを実行し、おそらく必要な環境変数が変更された後(親にも影響しない)、調整された環境で新しいプログラムを実行できます。
ここの誰もがフォークがどのように機能するかを知っていると思いますが、問題はフォークを使用して親の正確な複製を作成する必要があるのはなぜですか? 回答 ==>サーバーの例(フォークなし)を使用して、クライアント1がサーバーにアクセスしているときに、2番目のクライアント2が同時に到着し、サーバーにアクセスしたいが、サーバーが新しく到着したサーバーに許可を与えない場合サーバーがクライアント1を処理するためにビジーであるため、クライアント2は待機しなければなりません。クライアント1へのすべてのサービスが終了した後、クライアント2はサーバーにアクセスできるようになりました。 client-3が到着するため、client-3はclient-2へのすべてのサービスが終了するまで待機する必要があります。数千のクライアントが同時にサーバーにアクセスする必要があるシナリオを考えます。 wait(サーバーはビジーです!!)。
これは、(フォークを使用して)サーバーの正確な複製コピー(子)を作成することにより回避されます。各子(親、つまりサーバーの正確な複製コピー)は、新しく到着したクライアント専用であるため、同時にすべてのクライアントが同じサーバ。
fork
inetd