簡単なコマンドの実行に関して簡単な質問があります。私の理解によると、インタラクティブシェルでls
などのコマンドを入力すると、
私の理解が正しければ、シェルプロンプトで入力した単純なコマンドは子プロセスで実行され、コマンドの結果は現在のシェルの環境に影響しません。
もしそうなら、cd
のような組み込みコマンドはどうですか? cd
が子プロセスで実行され、現在のシェルの環境に影響を与えられない場合、現在のシェルの作業ディレクトリをどのように変更できますか?
シェルは単なるプログラムですが、システムで重要な役割を果たします。 Bashと他のほとんどの一般的なシェルはCで実装されていると思います。サブプロセスの作成に使用される2つの最も重要なネイティブC システムコール は、fork()
とexec()
。これらの関数は通常、Shellを含む高水準言語でも実装されています。
fork()
「フォーク」は、子プロセスとして呼び出しプロセスの複製コピーを作成します。これは、最初のプロセス( init )を除く、システム上のすべてのプロセスがどのように開始するかです。それらを開始したプロセスのコピーとして。シェル言語には実際にはfork
関数はありませんが、サブシェルを生成するための構文が含まれていますが、これらは同じです。
exec()
Cには実際にはexec()
の呼び出しはありませんが、口語的には関連する関数のグループを指します。リストは_man 3 exec
_で確認できます。通常は次のように始まります:
Exec()ファミリーの関数は、現在のプロセスイメージを新しいプロセスイメージに置き換えます...
そして、それはまさにそれがすることです:replaces定義する現在のプロセスのメモリスタックの一部を、実行可能ファイル(たとえば、_/usr/bin/ls
_)からロードされた新しいもので置き換えます。これが、新しいプロセスを作成するときに最初にfork()
が必要な理由です。そうでない場合、呼び出しプロセスは元のプロセスでなくなり、代わりに別のプロセスになります。新しいプロセスは実際には作成されません。
最初は、ばかげた非効率的な方法のように聞こえるかもしれません。新しいプロセスを最初から作成するコマンドを用意しないのはなぜですか。実際、いくつかの理由により、これはおそらくそれほど効率的ではありません。
カーネルは copy-on-write システムを使用するため、fork()
によって生成される「コピー」は少し抽象化されています。実際に作成する必要があるのは、仮想メモリマップだけです。コピーがすぐにexec()
を呼び出す場合、プロセスが必要とする処理を何も行わないため、プロセスのアクティビティによって変更された場合にコピーされたであろうほとんどのデータを実際にコピー/作成する必要はありません。使用する。
子プロセスのさまざまな重要な側面(たとえば、その環境)は、コンテキストなどの複雑な分析に基づいて個別に複製または設定する必要はありません。これらは、呼び出しプロセスのそれと同じであると想定されているだけで、これは私たちがよく知っているかなり直感的なシステムです。
生成された子プロセスに「環境をコピー」することの正確な意味の詳細については、 ここに私の回答 を参照してください。
もしそうなら、cdのような組み込みコマンドはどうですか?
これらも、Cで実装されています。chdir()
は、fork()
やexec()
と同様に、標準CのUnixプラットフォーム拡張の一部であり、シェルの基礎となるものです。 cd
コマンド。 _man 2 chdir
_から:
chdir()は、呼び出しプロセスの現在の作業ディレクトリを、パスで指定されたディレクトリに変更します。
これはサブプロセスを必要としません-呼び出し側に影響します。シェルは対話型ランタイム インタープリター です。つまり、コマンドを入力すると、シェル言語で記述されたコードが実行されます。これを行うために新しいプロセスを実行する必要はほとんどありません。それ自体でそれを行います。
もしそうなら、cdのような組み込みコマンドはどうですか? cdが子プロセスで実行された場合
ここにあなたの論理の欠陥があります。シェルに組み込まれているcd
は、シェルによって特別に解釈され、インプロセスで「実行」されます。 type
コマンドを使用して、コマンドが組み込みかどうかを確認できます。