分解させてください。
あなたが実行可能ファイルを実行すると、システムコールのシーケンスは、最も顕著なのは、実行されているfork()
とexecve()
:
fork()
(ほとんど)親の正確なコピーである呼び出しプロセスの子プロセスを作成し、両方とも同じ実行可能ファイルを実行します(コピーオンライトメモリページを使用するため、効率的です)。2回返します。親では、子PIDを返します。子では、0を返します。通常、子プロセスはすぐにexecveを呼び出します。
execve()
引数として実行可能ファイルへのフルパスを取り、呼び出しプロセスを実行可能ファイルに置き換えます。この時点で、新しく作成されたプロセスは独自の仮想アドレス空間、つまり仮想メモリを取得し、そのエントリポイントで実行が開始されます(プラットフォームABIの新しいプロセスのルールで指定された状態)。
この時点で、カーネルのELFローダーは、システムコールを使用したかのように、実行可能ファイルのテキストセグメントとデータセグメントをメモリにマップしましたmmap()
(共有読み取り専用マッピングとプライベート読み取り/書き込みマッピングをそれぞれ使用)。BSSは、MAP_ANONYMOUSを使用する場合と同様にマップされます。(ところで、簡単にするためにここでは動的リンクを無視しています。動的リンカーは、メインの実行可能ファイルのエントリポイントにジャンプする前にすべての動的ライブラリをopen()
sおよびmmap()
sします。)
新しく実行された()が独自のコードの実行を開始する前に、実際にディスクからメモリにロードされるページはわずかです。プロセスがその仮想アドレス空間のそれらの部分に触れる場合/その場合、必要に応じて、さらにページが要求されます。(ユーザー空間コードの実行を開始する前にコードまたはデータのページをプリロードすることは、パフォーマンスの最適化にすぎません。)
実行可能ファイルは、下位レベルのiノードによって識別されます。ファイルの実行が開始された後、カーネルは、開いているファイル記述子やファイルバックアップメモリマッピングの場合のように、ファイル名ではなくiノード参照によってファイルの内容をそのまま保持します。そのため、実行可能ファイルをファイルシステムの別の場所、または別のファイルシステムに簡単に移動できます。補足として、プロセスのさまざまな統計情報を確認するに/proc/PID
は、ディレクトリ(PIDは指定されたプロセスのプロセスID)を覗きます。実行可能ファイルは/proc/PID/exe
、ディスクからリンクされていなくても開くことができます。
では、動きを掘り下げましょう。
同じファイルシステム内でファイルを移動するとrename()
、実行されるシステムコールはで、ファイルの名前を別の名前に変更するだけで、ファイルのiノードは同じままです。
一方、2つの異なるファイルシステムの間では、2つのことが起こります。
rm
実際にはunlink()
、ディレクトリツリーから特定のファイルを削除するだけです。そのため、ディレクトリに対する書き込み権限があると、そのディレクトリからファイルを削除する十分な権利が得られます。
楽しみのために、2つのファイルシステム間でファイルを移動しunlink()
、ソースからのファイルへのアクセス許可がない場合に何が起こるか想像してみてください。
さて、ファイルは最初に宛先にコピーされ(read()
、write()
)、その後、unlink()
権限が不十分なため失敗します。したがって、ファイルは両方のファイルシステムに残ります!!