POSIX 環境のプロセス管理に関する考察


[Up]
POSIX 環境のプロセス管理に関する考察
					Dec 23, 2001	T. Naniwa

I. init 側からたどった fork/exec に関する疑問点とメモ

・kernel/BTRON/init/posix_if.c の posix_fork で cre_tsk()に失敗した場
  合などに ter_tsk() を呼んでいるが,これは del_tsk() を呼ぶべきではな
  いか?
    ter_tsk() で,タスクが待ち状態にある場合の処理が実装されていない.
      messge 待ちと semaphore 待ちに対する処理を追加した.

・同じく posix_if.c: posix_fork() は lowlib を経由せずに直接 POSIX
  manager にメッセージを送っている.

・signal_task には lowlibinfo->signal_entry_addr が保存されるが,
#define LOWLIB_INFO_ADDR        0x88030000
  は修正途中.kernel/ITRON/h の lowlib.h で posix manager 用の大域変数
  lowlib_data が用意されている.
#define LOWLIB_DATA             (struct lowlib_data *)(0x7fff0000)
  を使っている.
      LOWLIB_INFO_ADDR は使わず,LOWLIB_DAT を使用する.

・POSIX 環境の fork システムコールは生成された ITRON task を二つ(main,
  signal)引数として受け取るようだ.その後 task を起動 (sta_tsk())してい
  る(init 側の処理).
      signal タスクは現在生成していない.おそらく,今後も使用しないだろ
      う.

II. POSIX manager 側からたどった疑問点とメモ

II-1. fork の処理

・proc_alloc_proc で新しいプロセスを確保.
   free リストから割り当てるだけ.

・setup_vm_proc で仮想空間の生成.
   create_vm_tree を呼び出す.パラメータに VM_SHARE あり.

・duplicate_memory で仮想空間のコピー
   duplicate_tree を呼び出す.

・ITRON タスクに対しては何の動作も行なわないようだ.
   sta_tsk() は fork の最後で実行する.

II-2. exec の処理

・read_exec_header でファイル情報を読み込み

・proc_destroy_memory でプロセスのメモリを開放

・proc_renew_task でタスクの生成.
   引数に
	req->param.par_exec.start_main
	req->param.par_exec.start_signal
   がある.
   lowlib の system 部は未作成.pathlen, path, procid は送る必要あり.

・load_text でテキスト部を読み込み

・load_data でデータ部を読み込み

・ITRON の拡張システムコール vset_ctx でタスクのコンテキストを書き換え
  る.POSIX manager は元のタスクにメッセージは返信しない.

III. その他の疑問点とメモ

・POSIX プロセスも普通の ITRON タスクになっているが,スタックはカーネル・
  スタック領域を使い続けるのか?
    固有のスタックを生成するように修正した.fork が vcpy_stk を呼び出し
    たところで,新しい stack を割り当てる.
    POSIX の vmtree では管理されない.

・親タスクの ADDR_MAP をコピーして使用しているが,これだとタスク(プロセ
  ス)の権限が周辺核の権限のままになる.
   lowlib でタスクを作ると周辺核権限になるので,タスクの実行時にユーザー
   モードに移行する必要がありそう.

・exd_tsk() システムコールはあるので,タスクの終了はできそう.
   タスクのスタックを開放する必要がある.
      pfree を呼ぶ前に p & 0x7FFFFFFFUL する必要がある.
         また,pfree の引数の size はページ数.
      T_TCB が stksz と stackptr でスタック情報を保持するようになっている.

・実行中のタスクのモニタリング用に,vsys_msc の 6 番に ITRON/common/task.c
  の print_task_list を割り当てた.

・POSIX プロセスが終了する前には destroy_vmtree を呼ぶ.この中身も作る
  必要がある.
   destroy_vmtree は実行中のプロセス/タスクの vmtree の破棄にも呼び出される.
   vmtree のために malloc された領域を free する必要がある.
   ITRON の vunm_reg(), vdel_reg() を呼び出す.
     初期の region はカーネル領域用のものとし,execve でロードする
     テキスト,データ,スタック用に別々の region を設定する.

・vdel_reg(id, start) task(id) から start を含む region を削除する.
   対応するテーブルの要素を見つけ,permission に 0 を代入すれば良い.

・vunm_reg(id, addr, size) タスクに割り当てられた物理メモリを開放する
   vmap_reg で palloc(1) した領域を pfree で開放する.
   ページディレクトリのエントリーの開放のために vunmap を呼び出す.

・vunmap(id, addr) 仮想メモリを開放する.
   ページディレクトリのエントリーを開放する.palloc(1) で割り当てられて
   いるので pfree で開放.

・config.h で SYSTEM_MEMORY_SIZE が 2M になっているが,ここを書き換え
  ればカーネルが認識するメモリー領域を変更できる.実際には 4MB の領域
  までデバイスドライバーなどがロードされている.
    現在は 12 MB を OS で使用する設定になっている.conventional memory
    も kernel image (itorn.image) で使用している.

IV. fork/exit の仕様設計

IV-1. fork で行なっている処理

・main task の生成
   この段階では stack 領域が確保されるだけ.task の本体はない.

・main task に呼び出したタスクのメモリーイメージを複写(duplicate_tree()).
   task の本体が出来上がる.

・file table の複写

・親タスクのスタック・イメージのコピー.コンテキストの esp, ebp を新たな
  スタック用に設定する.fork 後 execve または exit する事が前提となる.
  スタックをプロセス固有の領域に置けば解決できるかも.

・main task をスタート

検討を必要とする点
・初期 file table の準備 (lowlib)
    init でファイルディスクリプタの 0, 1, 2 を疑似的にオープンしている.

・スペシャル・ファイルの扱い方 (POSIX 環境)
    filesystem.c の special_file_table に予め major/minor number とドラ
    イバーの名称が登録されている.

・子プロセスのスタート・ポイントの設定 (libc/lowlib)
    実行権限をユーザーモードに落とす必要がある.

・親プロセスの region のコピーをどうするか?
   vdup_reg を実装した.
   vdup_reg の第 3 引数には region 番号を与える.
     vdup_reg は使っていない.exec システムコールで,region は再設定され
     る.

IV-2. exit に必要な処理

・file のクローズ (POSIX 環境)

・親プロセスの waitpid にメッセージを送る (POSIX 環境)

・子プロセスの親プロセスを変更 (POSIX 環境)

・スケジューリングの停止 (ITRON)

・メモリーイメージの開放 (POSIX 環境)
   vmap の開放は ITRON の del_tsk で行なわれる.

・stack の開放(ITRON)

・task の終了と削除

・プロセス表のクリア

解決すべき問題点
・シグナルの扱い
・子プロセスの INIT への付け替え

IV-3. waitpid に関する考察

・waitpid システムコールの戻り値の生成方法
    メッセージ送信を使うのか

・POSIX init の扱い方.process tree の root になるのか?

・waitpid 処理の流れ
1. libc/waitpid() を呼び出す.
2. int $65 で lowlib に移行.
3. psys_waitpid から _make_connection で POSIX/manager を呼び出す.
    snd_mbf でメッセージを送り,rcv_mbf で返事を待つ.
4. POSIX/manager の waitpid で,
    対応する子プロセスが既に exit していた場合,pid と status を
      メッセージとして返して終了.
    exit したプロセスが無い場合,プロセス表に書き込みをし,メッセージ
      を返さずに終了.
5. POSIX/manager の exit で,プロセスのいずれかが終了し,親プロセスの 
   waitpid の条件にあう場合,pid と status をメッセージとして送信.
6. lowlib の psys_waitpid はメッセージを受け取り,pid を return.
7. libc/waitpid に戻り,call_lowlib の結果をそのまま retrun.

解決すべき問題点
・プロセス・グループの扱い方法

IV-4. execve に関する考察.
・ELF バイナリーで,テキスト部は PF_R+PF_X,データ部は PF_R+PF_W のフラグ
  を持つようだ.

・ファイルからバイナリーイメージをメモリーに読み込んだ後,タスクの状態の
  初期化が必要になる.待ち状態の解除,コンテキストの初期化が必要.

検討を要する点
・main に渡す argc, argv, envp はスタックに書き込む.
   POSIX 環境で argv, envp を作成するのは面倒なので,libnative で argv, 
   envp を含めたメモリーイメージを作成して POSIX manager に送る.

・新しいバイナリーに対応する region の設定をどうするか.
   text, data+bss に関してはファイルから読み込んだ値から region の start 
   と size を計算する.
   heap と stack についてはメモリー上での region の配置を予め決めている.

IV-5. page fault 発生時のプロセスの強制終了に関する考察.
・page fault が発生したときには T_TCB に登録されている page_fault_hander
  が呼び出される.これは割り込みのコンテキストで実行される.

・page_fault_hander に登録する関数は ITRON 中心核で定義されたものが良
  いだろう.そうでないと,ITRON の機能を使おうとしたときにまた TRAP が
  発生することになる.

・PAGE FAULT 発生時の処理は当面はカーネル側で行う.シグナル処理用のタス
  クで実行しようとすると,シグナル処理中に PAGE FAULT が起こった場合に
  対応させることが困難になる.シグナル処理そのものも別タスクで行うより,
  主タスクに割込み処理のスタックフレームとコンテキストを生成して実行さ
  せる方が良さそうだ.

・カーネル側で行う処理は以下となる.
  1) PAGE FAULT を起こしたタスクの強制終了 (ter_tsk, del_tsk). 
  2) POSIX manager へのタスクの終了の通知.
     POSIX の PID を通知する.
     POSIX manager 内部での処理は kill システムコールで行う.実態は exit 
     システムコールとほぼ同じ.
     POSIX manager への通知には message 送信を行う必要がある.
       POSIX manager のメッセージキューが空でない場合やサイズが 0 の場合
       がある.
       強制終了通知のメッセージは他のアプリケーションのメッセージよりは
       優先されなければならない.
       POSIX manager から KERNEL_TASK に応答を返す必要は無い.
  3) コンテキスト・スイッチによる次のタスクの実行.
     slp_tsk によって PAGE FAULT を起こしたタスクを待ち状態にし,そこで
     コンテキストスイッチを起こさせる.

  実際の処理の順番は 3) → 2) → 1) となっている.

・POSIX manager へプロセスが終了したことを知らせるメッセージの送信には,
  待ち状態に入らない psnd_mbf を用いるのが良いだろう.この場合,POSIX
  manager がメッセージを受け付けるようになるまで繰り返し psnd_mbf を実行
  する必要があるため,task id 1 で処理を行う必要がある.

・POSIX manager にメッセージを送る必要が生じた場合,task id 1 の優先度を
  POSIX manager と通常のタスクの間の優先度に引き上げる.

・複数のアプリケーションで PAGE FAULT が発生する可能性があり,POSIX
  manager へのメッセージ送信のための情報の待ち行列を作る必要がある.
    現在の実装では,待ち行列の大きさを 10 としている.

※ 以下の以前の考察は無効になる可能性大.

・PAGE FAULT 発生時のアプリケーションの終了の処理をシグナル処理用タス
  クに実行させるなら,そのエントリーは LOWLIB の関数として定義し,タス
  クそのものを LOWLIB の中で生成するのが良いだろう.

・page_fault_hander では割り込みの発生したタスク(メインタスク)を 
  SUSPEND し,シグナル処理用のタスクのタスクを sta_tsk する.シグナル
  処理用タスクでは,POSIX 環境に exit システムコールを発生し,メインタ
  スクを ter_tsk, del_tsk し,自タスクも exd_tsk する.

・dup_vmap_table は KERNEL 領域の page table しかコピーしていない.シ
  グナル処理用タスクがユーザー領域をメインタスクと共有するようにするた
  めには,dup_vmap_table とは別のルーチンを使って page table をコピー
  する必要がある.このとき,物理ページを共有する方法を考える必要がある
  だろう.さらに,release_vmap の中で物理ページを解放するタイミングを
  正しく設計する必要がある.release_vmap は del_tsk, exd_tsk の中で呼
  び出される.

・シグナル処理用タスクを LOWLIB で生成した場合,メインタスクで exit 
  を実行したときにシグナル処理用タスクを終了させる処理を追加する必要が
  ある.

・POSIX のプロセステーブルにはメインタスクとシグナルタスクのタスク ID 
  を保存する領域がある.この情報は KILL システムコールを実装するために
  必要.この情報を正しく設定するには,これらのタスクを fork の前に生成
  する必要がある.exec 後にユーザー領域のメモリーを共有するためには,
  シグナルタスクのページテーブルを修正する必要がある.

V. プロセス・スケジューリングに関する考察

V-1. プロセススケジューラで必要な処理

1) プロセス(タスク)の実行時間の計測
   タスクのコンテキストスイッチは POSIX manager には関係なく行われるの
   で,それぞれのタスクの実行時間は ITRON カーネル側で計測する必要があ
   るだろう.

2) タスク・スイッチの条件判定
   POSIX プロセスに対して,次の優先順位のプロセスへのスイッチを起こす
   かどうかの判定を行う必要がある.

3) タスクの実行順位の変更
   ITRON 部のレディーキューに含まれているタスクの順番を変える.
   rot_rdq でレディーキューを回転させることが出来るが,このときキュー
   の先頭のタスクが最後尾につながれる.

V-2. スケジューリング・ポリシーについて.

・ ITRON 部では,優先度を絶対とした FCFS (First Come First
   Service) 方式のスケジューリングを行っている.

・ OS の構成要素としては,デバイスドライバの優先度が最高で,ついで各マ
   ネージャ,ユーザープロセス(POSIX アプリケーション),最後が
   KERNEL_TASK(task id 1) となっている.但し,KERNEL_TASK の優先度は必
   要に応じて変化している.

・ POSIX アプリケーションの優先度の最高値は制限されるが,優先度をそれ
   以下に設定することは自由である.

・ プロセススケジューラが最低限保持しなければならない機能は,入出力を
   行わない計算だけのユーザープロセスが CPU を占有し続けることを妨げる
   機能である.

・ 「ユーザープロセスが一定時間連続して CPU を占有していた場合は別のプ
   ロセスへのタスクスイッチを発生させる」というポリシーのスケジューラ
   は,ITRON 中心核の改造だけで実現することができ POSIX マネージャを関
   与させる必要は無い.
     MINIX ではこれに近いポリシーを採用している.

V-3. 実装において検討すべき点.

・ rot_rdq の呼び出しは,クロック割込みハンドラの中で行えるか?  
   基本的にはキューの回転程度の処理なので,KERNEL_TASK で行わなくても
   大丈夫なのではないか.

V-4. 実装

・ ユーザープロセスは一定のクォンタム(実行時間)を与えられ, クォンタムを
   使い切ると次のプロセスにスイッチする.

・ クォンタムは,ユーザープロセスのコンテキストが設定されたとき,wup_tsk 
   で再起動されたとき,クォンタムを使い切ったときに再設定される.preempt
   された後で実行可能になっても,クォンタムは再設定されない.

・ クォンタムは 50 ms とした.

・ スケジューラは POSIX 環境には依存せず,ITRON カーネル部のみで実装した.