それで、私はこれをよく理解していると思いましたが、(誰かと意見が合わなかった会話に応えて)テストを実行したところ、私の理解に欠陥があることがわかりました…
可能な限り詳細に シェルでファイルを実行するとどうなりますか?つまり、次のように入力すると、 ./ somefile some arguments
シェルに入れてreturnキーを押します(そして somefile
cwdに存在し、 somefile
に対する読み取り+実行権限があります )それでは、内部で何が起こりますか?
私は考えた 答えは:
- シェルは
exec
へのシステムコールを行います 、パスをsomefile
に渡します - カーネルは
somefile
を調べます ファイルのマジックナンバーを調べて、それがプロセッサが処理できる形式であるかどうかを判断します - マジックナンバーが、ファイルがプロセッサが実行できる形式であることを示している場合は、
- 新しいプロセスが作成されます(プロセステーブルのエントリを使用)
-
somefile
読み取り/メモリにマッピングされます。スタックが作成され、実行がsomefile
のコードのエントリポイントにジャンプします。 、ARGV
を使用 パラメータの配列に初期化されます(char **
、["some"、 "arguments"]
)
- マジックナンバーがシバンの場合、
exec()
上記のように新しいプロセスを生成しますが、使用される実行可能ファイルは、シバンによって参照されるインタープリターです(例:/ bin / bash
または/bin / perl
)およびsomefile
STDIN
に渡されます - ファイルに有効なマジックナンバーがない場合、「無効なファイル(不正なマジックナンバー):Execフォーマットエラー」などのエラーが発生します
ただし、ファイルがプレーンテキストの場合、シェルはコマンドを実行しようとする( bash somefile
と入力したかのように)と誰かが教えてくれました。 )。信じられませんでしたが、試してみたところ、正しかったです。ですから、私は明らかにここで実際に何が起こっているのかについていくつかの誤解を持っており、メカニズムを理解したいと思います。
シェルでファイルを実行するとどうなりますか? (詳細は合理的です…)
承認された回答:
Linuxでの「プログラムの実行方法」に対する決定的な答えは、LWN.netの「プログラムの実行方法」と「プログラムの実行方法:ELFバイナリ」というタイトルの2つの記事です。最初の記事では、スクリプトについて簡単に説明します。 (厳密に言えば、決定的な答えはソースコードにありますが、これらの記事は読みやすく、ソースコードへのリンクを提供します。)
少し実験してみると、ほぼ正しく理解できており、コマンドの単純なリストを含むファイルの実行は、シバンなしでシェルで処理する必要があります。 execve(2)のマンページには、テストプログラムexecveのソースコードが含まれています。これを使用して、シェルなしで何が起こるかを確認します。まず、テストスクリプト testscr1
を記述します。 、含む
#!/bin/sh
pstree
もう1つは、 testscr2
、のみを含む
pstree
両方を実行可能にし、両方がシェルから実行されることを確認します。
chmod u+x testscr[12]
./testscr1 | less
./testscr2 | less
execve
を使用して、もう一度やり直してください (現在のディレクトリにビルドしたと仮定します):
./execve ./testscr1
./execve ./testscr2
testscr1
引き続き実行されますが、 testscr2
生成する
execve: Exec format error
これは、シェルが testscr2
を処理することを示しています 別の方法で。ただし、スクリプト自体は処理しませんが、 / bin / sh
を使用します。 それをするために;これは、 testscr2
を配管することで確認できます。 less
:
./testscr2 | less -ppstree
私のシステムでは、
|-gnome-terminal--+-4*[zsh]
| |-zsh-+-less
| | `-sh---pstree
ご覧のとおり、私が使用していたシェル、 zsh
、 less
を開始しました 、および2番目のシェル、プレーン sh
(ダッシュコード> 私のシステムでは)、
pstree
を実行したスクリプトを実行します 。 zsh
で これはzexecve
によって処理されます Src / exec.c
内 :シェルは execve(2)
を使用します コマンドの実行を試み、それが失敗した場合は、ファイルを読み取ってシバンがあるかどうかを確認し、それに応じて処理し(カーネルも実行します)、失敗した場合は、 sh
、ファイルからゼロバイトを読み取らなかった場合:
for (t0 = 0; t0 != ct; t0++)
if (!execvebuf[t0])
break;
if (t0 == ct) {
argv[-1] = "sh";
winch_unblock();
execve("/bin/sh", argv - 1, newenvp);
}
bash
execute_cmd.c
で実装された同じ動作をします 役立つコメント付き(taliezinが指摘):
うまくいけばどこかのディスクファイルで定義されている簡単なコマンドを実行します。
fork()
- パイプを接続する
- コマンドを検索する
- リダイレクトを行う
execve()
execve
の場合 失敗した場合は、ファイルに実行可能モードが設定されているかどうかを確認してください。
その場合、ディレクトリではない場合は、その内容を
シェルスクリプトとして実行してください。
POSIXは、 exec(3)
と呼ばれる一連の関数を定義します。 execve(2)
をラップする関数 この機能も提供します。詳細については、muruの回答を参照してください。 Linuxでは、少なくともこれらの関数はカーネルではなくCライブラリによって実装されます。