Personal tools
You are here: Home ブログ 学習経過 カーネル(その8)
« December 2010 »
Su Mo Tu We Th Fr Sa
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  
Recent entries
sysfs tips 02 ohyama 2010-09-09
sysfs tips ohyama 2010-09-02
Haskell で周波数スペクトルを得る ohyama 2010-07-29
Haskell で線形識別関数の学習を行う ohyama 2010-07-19
Haskell で逆行列を求める ohyama 2010-07-16
Recent comments
Re:vim に lisp 機能をつける t.mimori 2010-12-16
Re:Haskell で周波数スペクトルを得る H.OHYAMA 2010-08-01
Re:lkml でお勉強 (その1-1) Hiroyasu OHYAMA 2009-08-21
Re:lkml でお勉強 (その1-1) kosaki 2009-08-20
Re:vim に lisp 機能をつける ohyama 2008-05-08
Categories
学習経過
GNU/Linux
 
Document Actions

カーネル(その8)

  前回は、GNU/Linux (以下、Linux) における ELF 実行処理について見てきた。今回は、共有ライブラリをロードしない ELF 実行形式ファイルを実行する環境を onix に構築する話をする。そして、実際に実行形式ファイルを Linux で作成して、onix で実行する。
 
  onix で ELF を実行する処理は elf.c に記述されている。elf_load_file() ルーチンでは、実行するプログラムのパスを受け取り、パス名検索を行って、ディレクトリツリーからディスク inode を取得。そして、ファイルの実態を取得している。
  そして、ファイルの ELF ヘッダ及びプログラムヘッダを解析し、実行ファイルによって共有された仮想領域に、各セグメントをマッピングさせる。
  ここでの完全に独立した(共有ライブラリのロード及び、実行時ロードを行わない) ELF ファイルの実行処理は、linux における load_elf_binary() 処理とやっている事はほとんど同じである。ただし、各種ファイルチェック及びセキュリティチェック、そして共有ライブラリのロード処理に関する処理については、ここでは一切行っていない。
  このように、ロード処理は極めて単純な仕組み及び処理になっている。
 
  ここで話が逸れるが。プログラムのページマッピング処理を作っている最中に onix におけるページ管理の仕組みを大幅に変更した。現在の拡張ページテーブルにおける設計が限界を迎えた為である。
  そもそも、何故この仕組みを使おうとしたかというと、半年前に遡る。
  当時、スワップアウト時にページを圧縮して、ディスクアクセスを減らし、スワップ時のパフォーマンス劣化を防ぐ機能を持たせる事で、onix の存在意義を訴えようとしていた。ページフォルトによるスワップイン処理はハードウェアによってページ単位で処理が行われるので、スワップ時のパフォーマンスを上げる際には、圧縮するページのサイズが大きい程パフォーマンスが向上する(と思われる)。
  それによって、IA-32 に存在する拡張ページテーブルの機能によって、仮想領域へのアクセスによるアドレス解決を 1 段階のページテーブルによって実現した。これによって、ページサイズは (2^22 = 4MB) になる。
  だが、これを実現した場合、ユーザーに配れるページ数が極端に制限される。なぜならば、複数のプロセスによって同一の物理メモリを参照した場合には、メモリ保護を行う事が (恐らく) 不可能である為である。
  例えば、1024 MB のメモリを搭載したマシンで onix を動かしたときを考える。搭載されたメモリの 3/8 はカーネルによって使われ、5/8 がユーザーが使える領域となるので、一つのプロセスが一つのページを利用した場合でも、最大で 160 個のプロセスしか起動できない事になる。
  数 TB のメモリを積んだ 64 bitマシンで、頻繁にページテーブルがストレートマッピングされるような環境でも無い限り、この設計ではとても運用できない。
  そこで、ユーザーランドのメモリ領域として予約してあるシステムの全体の 5/8 のメモリ領域のみ通常のページング方式によるマッピングを行い、それ以外のカーネル領域では拡張ページング方式によるアドレス解決を行う事にした。
  カーネル領域において、従来通りの方法を採った理由は二つ。一つは、カーネル領域をアクセスするのはカーネルだけであるので、カーネルのメモリ管理機構によって拡張ページをスラブに分割してカーネルに供給する分には、ユーザー側で発生するような問題は発生せず、物理メモリの先頭からストレートマッピングしているだけの構造において、ページテーブルを参照するのは無駄である。という理由と、書き直すのが面倒という理由によって、拡張ページと通常ページが混在するシステムとなった。
 
  次に、ELF 実行形式ファイルを Linux 側で作成して onix で実行する。
  onix には、現在シェルが存在せず、カーネルモードによって実行するデバッグ用のシェルしかない。よって次のようにしてプログラムを実行する。
  まず、プログラムカーネル起動時にプロセスを一つ生成して、デバッグシェルを起動。そして、"start timer" コマンドによってキューに存在する他のプロセス (プログラムファイルを実行するプロセス) に切替える。そして、プログラムを実行するプロセスは、プログラムの処理を終えたならば戻り値を受け取り、その値を表示する。
  Linux による外部プログラムの実行時に処理が行われる load_elf_binary() では、システムコールのサービスルーチンから直接呼び出し、プロセスの制御を外部プログラムに移しているが。ここでは、ユーザープロセスから直接ロードを行って、エントリポイントを関数のポインタとしてユーザープログラムに渡し、関数呼び出しでプログラムの処理を実行する。
  上述したように、ここで実行させるプログラムは、共有ライブラリをリンクせず、自前のコードのみを実行するプログラムである。とはいっても、出力に何も表れないのはさみしいので、write システムコールを呼び、標準出力に文字列を表示させる。コードは以下のようになる。
 

  int main()
  {
  write(1,"FOO BAR baz\n",12);
 
  return 0xaabbccdd;
  }

    当然だが、write のラッパールーチンも自前で書かなければならない。なので、次のコードを静的にリンクする。

  .code32
 
  .text
 
  .global write, _start
 
  _start:
  jmp main
 
  write:
  movl $0x4, %eax            # syscall number
  movl 0x04(%esp), %ebx        # first argument
  movl 0x08(%esp), %ecx        # second argument
  movl 0x0c(%esp), %edx        # third argument
  int $0x80
  ret

 
  そして、-nostdlib オプションをつけてコンパイル・リンクして得た実行ファイルを onix で実行すると、次の用になる。
 
screen_shot_1125
 
  このようにカーネル側で、ユーザープロセスに関するメモリ管理機構が機能していれば、この手のプログラムを実行するのは難しい話では無い。しかし、動的リンクを行う場合にはちょっと話が難しくなる。Linux では、そのあたりをカーネル側ではサポートせずに、ライブラリに依存した形で実現している。これについては、次回以降で話をする。

Category(s)
学習経過
The URL to Trackback this entry is:
http://dev.ariel-networks.com/Members/ohyama/30ab30fc30cd30eb-305d306e8/tbping
Add comment

You can add a comment by filling out the form below. Plain text formatting.

(Required)
(Required)
(Required)
(Required)
(Required)
This helps us prevent automated spamming.
Captcha Image


Copyright(C) 2001 - 2006 Ariel Networks, Inc. All rights reserved.