20章 プログラムの実行

  • カーネルがプログラムファイルの内容に従ってプロセスの実行コンテキストを準備する方法を説明
  •  カーネルが必要とする機能
    • 複数の実行形式:他のOS用にコンパイルされたバイナリを実行できる
    • 共有ライブラリ:ライブラリからプログラム実行に必要な関数をメモリに読み込む機能
    • 実行コンテキストのその他の情報:コマンドライン引数や環境変数など
  • 実行ファイル:実行する関数のオブジェクトコードとその関数が処理を行うデータを持つ
  • プログラム関数のオブジェクトコードはライブラリの中に入っている
    • 実行ファイル内に静的に取り込む方法(スタティックライブラリ)
    • 実行時にプロセスとリンクする方法(共有ライブラリ)
  • ユーザーはプログラム実行時に、コマンドライン引数と環境変数を与えることが出来る

20.1 実行ファイル

  • 実行ファイル:新しい実行コンテキストの初期化方法を記述している通常ファイル
    • 実行コンテキスト:ある処理を行う際に必要な情報の集まり
  • ユーザがコマンドシェルを操作すると、新しいプロセスを生成し、コマンドをexecve()システムコールに渡す
  • execve()システムコールは、プログラムの実行のためにカレントプロセスの実行コンテキストを書き換える
  • プロセスが新しいプログラムを実行すると、それまでの実行コンテキストはほとんど破棄される

20.1.1 プロセスの権限とケーパビリティ

  • 権限:それぞれのプロセスが持つ、各ユーザのデータ保護とシステム全体の安定性を保つ機能
    • ファイルアクセス権限など
    • プロセスのUID,GIDをアクセスしようとするファイルのUID,GIDと比較してアクセス可能か調べる
    • setuid(),setresuid(),setfsuid(),setreuid()システムコールで実行IDを変更できる
20.1.1.1 プロセスのケーパビリティ
  • 特定の操作や特定の操作クラスを実行することをプロセスに許可するかどうかのフラグ
    • su権限が一部分だけ必要な操作のために一部の操作を提供する
  • VFSやExt2はケーパビりティを提供していないが、利用することが出来る
  • Linuxカーネルはケーパビりティを利用できる
20.1.1.2 Linux セキュリティモジュール基盤
  • LinuxのケーパビリティはLinuxセキュリティモジュール基盤に統合されている
  • セキュリティフック:セキュリティに関わる操作を実行するか判断するカーネル関数

20.1.2 コマンドライン引数とシェル環境

  • プログラム実行時にシェルからコマンドライン引数を受け取ることが出来る
  • コマンドライン引数は新しいプロセス実行時に新しいプロセスに引き継がれる
  • 環境変数:プロセスの実行コンテキストを調整する場合や、ユーザが他プロセスに情報を流すときなどに利用する引数

20.1.3 ライブラリ

  • 高級言語のソースコードは全てオブジェクトファイルに変換される
    • オブジェクトファイルはリンカを通じて、プログラムが利用するライブラリ関数の位置を取得する
  • 静的ライブラリ:リンカによって生成されるプログラムは、利用するライブラリ関数のコードも含む
    • プログラムサイズが大きくなる
  • 共有ライブラリ:実行ファイルにはライブラリの参照だけが含まれ、プログラム実行中に動的リンカがライブラリを取得
    • 実行に比較的時間がかかる、プログラムの互換性が低くなる

20.1.4 プログラムセグメントとプロセスメモリリージョン

  • リニアアドレス空間はセグメントに分割されている
    • テキストセグメント:プログラムの実行可能なコードを配置
    • 初期値ありデータセグメント:初期化されたデータを配置
    • 初期値なしデータセグメント;初期化されていないデータを配置
    • スタックセグメント:プログラムスタックを配置
20.1.4.1 柔軟なメモリリージョン割り付け
  • 各プロセスはユーザモードスタックの成長度に合わせてメモリリージョンの割り付けを行う
  • 最大2GBまで割り付けできる(従来のえ割り付け方法では1GB)

20.1.5 実行の追跡

  • あるプログラムが別のプログラムの実行を監視する方法
    • 主にデバッガが利用
    • ptrace()システムコール
  • 追跡されるプログラムの事象を監視
    • アセンブリ命令の実行、終了
    • システムコールの開始、終了
    • シグナルの受信

20.2 実行形式

  • ELF(Executable Linking Format):Linux標準の実行形式
  • EXEなどのほかの実行形式も実行可能
  • ユーザ独自の実行形式を登録することも出来る
    • ファイル先頭の128バイトのマジック番号を用いる方法と、拡張子を用いる方法がある
    • 独自の実行形式のファイルは、その実行形式に対応するインタプリタプログラムを起動する

20.3 実行ドメイン

  • 他のOS用にコンパイルされたプログラムを実行するための2つの方式
    • エミュレート実行:POSIX準拠でないシステムコールを利用するプログラムを利用する場合
      • MS-DOSやWINDOWSなど
      • DOSemuやWineなどのエミュレータを呼び出して実行
    • ネイティブ実行:システムコールが完全にPOSIX準拠の場合
      • Linux以外のOSでコンパイルされていても動作する
      • 仕様の差(システムコール呼び出し方法など)は実行ドメインディスクリプタで管理

20.4 Exec関数

  • プロセスの実行コンテクストを新しい実行コンテクストに置き換える関数群
  • execve()関数以外はCライブラリで定義したラッパールーチン
  • sys_execve()サービスルーチンは実行ファイルのパス名のアドレス、コマンドライン引数と環境変数のポインタ配列などを受け取って新しいページフレームを確保し、do_execve()関数を呼び出す
  1. linux_binprmデータ構造を割り当てる
  2. 実行ファイルのdエントリオブジェクト、iノードオブジェクトを取得する
  3. ファイルがカレントプロセスから実行可能であり、現在書き込み中でないか調べる
  4. 最も負荷の低いCPUにカレントプロセスを移動
  5. ローカルディスクリプタテーブルを初期化
  6. linux_binprmを初期化
  7. 新しいページフレームにファイルのパス名や引数などを読み込む
  8. ファイル実行形式を認識し、linuxbpm構造体を開放して実行形式に対応するload_binaryメソッドに割り当てる

load_binaryメソッドの動作
  1. ファイル先頭のマジック番号を確認して実行形式を調べる
  2. 実行ファイルからプログラムのセグメント、必要な共有ライブラリ、動的リンカのパス名を取得
  3. 動的リンカのiノードオブジェクトを所得
  4. 以前の処理で使用していた資源を開放する
  5. プロセスのメモリリージョンの配置を決定して割り当てる
  6. 実行ファイルのコード、データセグメントをマッピングするメモリリージョンを作成
  7. 動的リンカ用の領域を確保
  8. プロセスのケーパビリティを決定
  9. カーネルモードスタックに保存されているユーザモードスタック値を新しいユーザモードスタック値に変更
  • execve()システムコールが終了した時点で古い実行コンテキストは存在しなくなっている
  • execve()システムコール終了後、動的リンカによって共有ライブラリを取得してからプログラムが動作する

メンバーのみ編集できます