web-dev-qa-db-ja.com

プログラムを実行するとどうなりますか?

ここでは、Windows、Linux、OSXで実行可能ファイルを実行するとどうなるかをまとめたいと思います。特に、操作の順序を正確に理解したいと思います。実行可能ファイル形式(PE、ELF、またはMach-O)はカーネルによってロードされると思います(ただし、 のさまざまなセクションは無視します) [〜#〜] elf [〜#〜] (Executable and Linkable Format)とその意味)、そして参照を解決するダイナミックリンカーがあり、次に__init実行可能ファイルの一部が実行され、次にメイン、次に__fini、そしてプログラムは完了しますが、それは非常にラフで、おそらく間違っていると確信しています。

編集:質問はCWになりました。私はLinuxをいっぱいにしています。 WinとOSXで同じことをしたい人がいたら、それは素晴らしいことです。

38
Stefano Borini

もちろん、これは非常に高く抽象的なレベルです。

Executable - No Shared Libary: 

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel allocates memory from the pool to fit the binary image into
  ->Kernel loads binary into memory
  ->Kernel jumps to specific memory address
  ->Kernel starts processing the machine code located at this location
  ->If machine code has stop
  ->Kernel releases memory back to pool

Executable - Shared Library

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel allocates memory from the pool to fit the binary image into
  ->Kernel loads binary into memory
  ->Kernel jumps to specific memory address
  ->Kernel starts processing the machine code located at this location
  ->Kernel pushes current location into an execution stack
  ->Kernel jumps out of current memory to a shared memory location
  ->Kernel executes code from this shared memory location
  ->Kernel pops back the last memory location and jumps to that address
  ->If machine code has stop
  ->Kernel releases memory back to pool

JavaScript/.NET/Perl/Python/PHP/Ruby (Interpretted Languages)

Client request to run application
  ->Shell informs kernel to run binary
  ->Kernel has a hook that recognises binary images needs a JIT
  ->Kernel calls JIT
  ->JIT loads the code and jumps to a specific address
  ->JIT reads the code and compiles the instruction into the 
    machine code that the interpretter is running on
  ->Interpretture passes machine code to the kernel
  ->kernel executes the required instruction
  ->JIT then increments the program counter
  ->If code has a stop
  ->Jit releases application from its memory pool

RouteNpingmeが言うように、レジスタはCPU内に設定され、魔法が起こります!

更新:ええ、今日はきちんと話すことができません!

36
Wayne

わかりました、私自身の質問に答えます。これは段階的に行われ、Linux(およびおそらくMach-O)に対してのみ行われます。個人的な回答に自由に追加して、賛成を得られるようにしてください(そして、現在はCWになっているので、バッジを取得できます)。

途中から始めて、残りを見つけたら構築します。このドキュメントは、x86_64、gcc(GCC)4.1.2で作成されています。

ファイルを開く、初期化

このセクションでは、カーネルの観点から、プログラムが呼び出されたときに、プログラムを実行する準備ができるまでに何が起こるかについて説明します。

  1. ELFが開きます。
  2. カーネルは.textセクションを探し、それをメモリにロードします。読み取り専用としてマークします
  3. カーネルは.dataセクションをロードします
  4. カーネルは.bssセクションをロードし、すべてのコンテンツをゼロに初期化します。
  5. カーネルは、制御をダイナミックリンカー(名前はELFファイル内の.interpセクションにあります)に転送します。ダイナミックリンカは、すべての共有ライブラリ呼び出しを解決します。
  6. 制御がアプリケーションに転送されます

プログラムの実行

  1. eLFヘッダーが実行可能ファイルのエントリポイントとして指定しているため、関数_startが呼び出されます。
  2. _startは(PLTを介して)glibcで__libc_start_mainを呼び出し、次の情報を渡します。

    1. 実際の主な機能のアドレス
    2. argcアドレス
    3. argvアドレス
    4. _initルーチンのアドレス
    5. _finiルーチンのアドレス
    6. atexit()登録用の関数ポインタ
    7. 利用可能な最高のスタックアドレス
  3. _initが呼び出されます

    1. call_gmon_startを呼び出して、gmonプロファイリングを初期化します。実行とはあまり関係ありません。
    2. __register_frame_info(eh_frameセクションアドレス、bssセクションアドレス)をラップするframe_dummyを呼び出します(FIXME:この関数は何をしますか?BSSセクションからグローバル変数を明らかに初期化します)
    3. __do_global_ctors_auxを呼び出します。この役割は、.ctorsセクションにリストされているすべてのグローバルコンストラクターを呼び出すことです。
  4. メインが呼び出されます
  5. メインエンド
  6. _finiが呼び出され、次に__do_global_dtors_auxが呼び出されて、.dtorsセクションで指定されているすべてのデストラクタが実行されます。
  7. プログラムは終了します。
26
Stefano Borini

Windowsでは、最初に画像がメモリにロードされます。カーネルは、必要になるライブラリ(「DLL」を読み取る)を分析し、それらもロードします。

次に、プログラムイメージを編集して、必要な各ライブラリ関数のメモリアドレスを挿入します。これらのアドレスは、.EXEバイナリにすでにスペースがありますが、ゼロで埋められているだけです。

次に、各DLLのDllMain()プロシージャが、依存関係の順序に従うように、最も必要なDLL)から最後まで1つずつ実行されます。

すべてのライブラリがロードされて準備が整うと、最終的にイメージが開始されます。現在何が起こるかは、使用される言語、使用されるコンパイラ、およびプログラムルーチン自体によって異なります。

3
Havenard

画像がメモリに読み込まれるとすぐに、魔法が引き継ぎます。

1
Brandon

正確な定義によっては、.NetやJavaなどの言語のJITコンパイラを考慮する必要があります。技術的に「実行可能」ではない.Net「exe」を実行すると、JITコンパイラがステップインしてコンパイルします。

0
Max Schmeling