なぜシェルはfork()を呼び出すのですか?


32

プロセスがシェルから開始されると、なぜプロセスを実行する前にシェル自体がフォークするのですか?

たとえば、ユーザーがを入力したときに、シェルが子シェルなしでgrepをgrep blabla foo呼び出すことができないのはなぜexec()ですか?

また、シェルがGUIターミナルエミュレーター内で自分自身をフォークするときに、別のターミナルエミュレーターを起動しますか?(pts/13開始などpts/14

回答:


34

execファミリメソッドを呼び出すと、新しいプロセスは作成されず、代わりexecに現在のプロセスメモリや命令セットなどが実行するプロセスに置き換えられます。

例として、grepexecを使用して実行したいとします。bashプロセス(個別のメモリ、アドレス空間を持っています)です。を呼び出すとexec(grep)、execは現在のプロセスのメモリ、アドレス空間、命令セットなどをgrep'sデータで置き換えます。つまり、bashプロセスはもう存在しません。その結果、grepコマンドの完了後にターミナルに戻ることはできません。これが、execファミリのメソッドが返らない理由です。execの後にコードを実行することはできません。それは到達不能です。


ほぼ問題ありません--- Terminalをbashに置き換えました。;-)
Rmano 14年

2
ところで、コマンドを使用して、最初にフォークせずにgrepを実行するようにbashに指示できますexec grep blabla foo。もちろん、この特定のケースでは、それはあまり便利ではありません(grepが終了するとすぐにターミナルウィンドウが閉じます)が、時々便利です(たとえば、sshを介して別のシェルを起動する場合など) / sudo / screen、および元のものに戻るつもりはありません、またはこれを実行しているシェルプロセスが複数のコマンドを実行することを決して意図していないサブシェルである場合)。
イルマリカロネン14年

7
命令セットには非常に具体的な意味があります。そして、それはあなたがそれを使用している意味はありません。
アンドリューSavinykh

@IlmariKaronenコマンドの引数と環境を準備するラッパースクリプトで役立ちます。そして、bashが複数のコマンドを実行することを意図していない場合、実際bash -c 'grep foo bar'にはexecを呼び出すことで、bashが自動的に行う最適化の形式があります
Sergiy Kolodyazhnyy

3

に従って、pts自分でチェックしてください:シェルで、実行します

echo $$ 

あなたのプロセスID(PID)を知るために、例えば

echo $$
29296

次に、たとえば実行してsleep 60から、別のターミナルで

(0)samsung-romano:~% ps -edao pid,ppid,tty,command | grep 29296 | grep -v grep
29296  2343 pts/11   zsh
29499 29296 pts/11   sleep 60

そのため、一般に、プロセスに関連付けられた同じttyがあります。(これはsleepあなたのシェルを親として持っているため、これがあなたのものであることに注意してください)。


2

TL; DR:新しいプロセスを作成し、対話型シェルで制御を維持するための最適な方法であるため

fork()はプロセスとパイプに必要です

この質問の特定の部分に答えるために、親で直接grep blabla foo経由exec()して呼び出された場合、親は存在するように捕捉し、すべてのリソースを含むそのPIDがに引き継がれgrep blabla fooます。

しかし、一般に約の話をしましょうexec()fork()。このような動作の主な理由fork()/exec()は、Unix / Linuxで新しいプロセスを作成する標準的な方法であり、これはbash固有のものではないためです。この方法は最初から導入されており、当時の既存のオペレーティングシステムの同じ方法の影響を受けていました。関連する質問に対するgoldilocksの答えを多少言い換えると、fork()新しいプロセスを作成するのは、リソースを割り当てる限りカーネルの作業が少なく、多くのプロパティ(ファイル記述子、環境など)-すべてができるため、簡単です親プロセス(この場合はbash)から継承されます。

第二に、対話型シェルに関する限り、フォークせずに外部コマンドを実行することはできません。ディスク上に存在する実行可能ファイル(たとえば、/bin/df -h)を起動するには、親などを新しいプロセスに置き換え、そのPIDや既存のファイル記述子などを引き継ぐexec()など、ファミリー関数の1つを呼び出す必要がexecve()あります。対話型シェルの場合、コントロールをユーザーに戻し、親の対話型シェルに引き継がせます。したがって、最善の方法は、経由fork()でサブプロセスを作成し、そのプロセスを経由で引き継ぐことexecve()です。対話型シェルPID 1156を経由して子供を生むだろうので、fork()PID 1157で、その後、呼び出しexecve("/bin/df",["df","-h"],&environment)可能おり、/bin/df -hシェルは唯一の出口と復帰制御に、それまでのプロセスを待つ必要がある今、PID 1157で実行します。

たとえばdf | grep、2つ以上のコマンド間にパイプを作成する必要がある場合は、2つのファイル記述子(pipe()syscall からのパイプの読み取りと書き込み)を作成し、何らかの方法で2つの新しいプロセスがそれらを継承できるようにする必要があります。新しいプロセスをフォークし、dup2()呼び出しを介してパイプの書き込み終了をstdout別名のfd 1にコピーしました(書き込み終了がfd 4の場合、実行しますdup2(4,1))。ときexec()卵には、df子プロセスがそのの何も考えていないだろうが起こるstdoutその出力が実際にパイプを行くこと(それ積極的にチェックしていない限り)意識せずに、それへの書き込みを。同じプロセスが、たまたまgrep、私たちを除いてfork()、FD 3とし、パイプの読み出し側を取るdup(3,0)産卵前grepexec()。この間、親プロセスはまだ存在し、パイプラインが完了したら制御を取り戻すのを待っています。

組み込みコマンドの場合、一般的にシェルはコマンドfork()を除いてそうではありませんsource。サブシェルが必要ですfork()

要するに、これは必要かつ有用なメカニズムです。

フォークと最適化の欠点

現在、これはなどの非対話型シェルでは異なりbash -c '<simple command>'ます。fork()/exec()多くのコマンドを処理する必要がある最適な方法であるにもかかわらず、コマンドが1つしかない場合はリソースの無駄です。この投稿からステファン・チャゼラスを引用するには:

フォークは、CPU時間、メモリ、割り当てられたファイル記述子の点で高価です...終了する前に別のプロセスを待機するだけのシェルプロセスがあると、リソースが無駄になります。また、コマンドを実行する別のプロセスの終了ステータスを正しく報告することが難しくなります(たとえば、プロセスが強制終了された場合)。

そのため、多くのシェル(だけでなくbash)を使用して、単一の単純なコマンドでそれを引き継ぐexec()ことbash -c ''ができます。上記の理由から、シェルスクリプトのパイプラインを最小限に抑えることをお勧めします。多くの場合、初心者が次のようなことをしていることがわかります。

cat /etc/passwd | cut -d ':' -f 6 | grep '/home'

もちろん、これはfork()3つのプロセスになります。これは簡単な例ですが、ギガバイトの範囲の大きなファイルを考えてみましょう。1つのプロセスではるかに効率的になります。

awk -F':' '$6~"/home"{print $6}' /etc/passwd

リソースの浪費は、実際にはサービス拒否攻撃の一種である可能性があります。特に、フォーク爆弾は、パイプラインで自身を呼び出すシェル関数を介して作成されます。現在、これはsystemd上のcgroupsのプロセスの最大数を制限することで緩和されています。Ubuntuもバージョン15.04以降で使用しています。

もちろん、それは分岐が単に悪いことを意味するものではありません。前に説明したように、これはまだ有用なメカニズムですが、より少ないプロセスで連続してリソースを減らしてパフォーマンスを向上fork()できる場合は、可能な限り避ける必要があります。

こちらもご覧ください


1

bashプロンプトで発行するコマンド(例:grep)ごとに、実際には新しいプロセスを開始し、実行後にbashプロンプトに戻ることを意図しています。

シェルプロセス(bash)がexec()を呼び出してgrepを実行すると、シェルプロセスはgrepに置き換えられます。Grepは正常に機能しますが、実行後、bashプロセスが既に置き換えられているため、コントロールをシェルに戻すことはできません。

このため、bashはfork()を呼び出しますが、これは現在のプロセスを置き換えません。

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