UNIX 6th code reading - trap.c

初めての方はこちらを参照 http://d.hatena.ne.jp/takahirox/20101024/1287923014

はじめに

12章です。12章は長いので2回に分けます。

今回はCで書かれたtrap(2693)を見ていきます。以前のエントリで書いたように、このtrapには785 or 799から飛んできます。

今回「シグナル」というキーワードが出てきますが、シグナルは13章で扱われます。今回は深追いしません。

前処理

  • 2693 : 引数は以前のエントリ参照
  • 2698 : PDP11/40ではno op. 浮動小数レジスタの退避を行うらしい
  • 2699-2700 : trapされたプロセスがユーザーモードならばdev(Vector addressで決まったPSの下位5bit以外をマスクした値。どの種のトラップが起きたかを示す)の4bit目(トラップbit. PS[4]. 2章参照?)をセットする
  • 2701 : r0のアドレスをu.u_ar0に格納しておく。u.u_ar0経由で各種レジスタにアクセスするため。各システムコールハンドラで使用する(はず)。2605-2613も参照

トラップの種類によって分岐

2702行目からはdevの値で分岐します。つまりどの種のトラップが起きたかによって適切な処理が行われます。

カーネルモードのトラップ

trap.cでカーネルモードのトラップを処理することは基本的に予想外のことです。通常は以前のエントリで書いたようにnofaultをセットしてtrap handlerに処理を行わせるようです。

  • 2715 : default. 予想外のトラップなのでpanicを呼び出す
    • 2716-2719 : APR7, トラップされたプロセスのPS, devの値をprintfで出力したのちpanicを呼び出す


カーネルプロセスは浮動小数点操作を行いません(たぶん)。

カーネルモードで浮動小数点例外が起こるのは、ユーザープロセスが実行した浮動小数点操作がカーネルプロセスに切り替わった後にトラップを起こした場合です。浮動小数点命令は時間がかかり、かつ、非同期で実行されるのでこういうことが起こりえます(?)。

なので、2796-のユーザーモード時の浮動小数点例外と違い、すぐにシグナル処理を行いません。シグナルだけ送っておいて後で処理をします。

(2784からのコメントでは、カーネルプロセスが浮動小数点例外を引き起こすケースがありうるというように読みとれます。要確認)

  • 2792 : 浮動小数点例外
    • 2793 : トラップが起きたプロセスに対しシグナルを送る
    • 2794 : returnする。2818-の処理が行われないことに注意
ユーザーモードのトラップ

case文のn+USERはユーザーモードのトラップを表します。nは500-549を参照。USERは2700でセットされたdev[4]のこと。

最初にバスエラーの場合を見ていきます。

  • 2721 : バスエラーの場合
    • 2722 : iにシグナルの種類をセット
    • 2723 : breakし、2818へ
    • 2818 : シグナル(i)を送る
    • 2821-2822 : シグナルがセットされているかチェックし、セットされているならばシグナル処理を行う
    • 2823 : トラップを引き起こしたプロセスの優先度を再計算する


不正命令、ブレークポイントorトレース、iot、emtの場合もバスエラーの場合と同様の処理が行われます。

ただし不正命令の場合は、不正命令を起こした命令(fuiword(pc-2), pcはトラップを起こした命令の次の命令を指す)がSETDで、かつ、不正命令が捕らえられていない(u.u_signal[SIGINS]が0)ならば、このトラップは無視します。この理由は2725からのコメントを参照。

電源異常とプログラムされた割り込みの場合はdefault処理、つまりpanicを呼び出して止まります。

セグメンテーション例外の場合は最初に特別な処理が行われます。この処理の詳細は後で確認する予定です。

  • 2810 : セグメンテーション例外の場合
    • 2811 : トラップを起こしたプロセスのspをaに格納
    • 2812-2814 : 詳細はいずれ確認。今は置いておく
    • 2815 : ここから先はバスエラーと同様
システムコール

353Pに引数の話が書かれています。

(b)の「trap命令に続くプログラム列の中に埋め込まれたワードの集合として」はこんな感じだと思います。

  trap arg1 arg2

(c)の「プログラムのデータ領域内のワードの集合として」はこんな感じだと思います。(追記:この絵は少し怪しいです。後日修正します)

Lions本でも書かれているように、コンパイルの時点で引数が決定しない場合(c)の間接システムコールを使います。直接呼び出すよりもオーバーヘッドが大きいです。

システムエントリテーブルが2910から設定されています。ここに各システムコールに対応したルーチンがセットされています。各システムコールのエントリはsysent構造体です。

struct sysent {
  int count ;
  int (*call)( ) ;
} sysent[64] ;

(*call)( )はシステムコールを処理するルーチン(システムコールハンドラ)のアドレスで、countはそのルーチンに対する引数の数を表します。countは引数をセットする2762, 2765あたりで使われます。

  • 2751 : システムコールの場合
    • 2752 : u.u_errorをリセット
    • 2753 : トラップを起こしたプロセスのPS[0]をリセット(0にセット)
    • 2754 : callpにトラップを起こした命令に対応したルーチンをセットする。sysentは2910で設定されている。sysent構造体は2667を参照のこと
      • fuiword(pc-2)はトラップを起こした命令をあらわす。077でandをとるということは、下位6bitのみを取得することをあらわす
    • 2755-2763 : 間接システムコールの場合。callpがsysent配列の先頭と等しいならば
      • 2756 : トラップを起こした命令の次の命令のアドレス?aに格納
      • 2757 : pcをさらに次の命令を指すようにする
      • 2758 : トラップを起こした命令の次の命令をiに格納
      • 2759-2760 : iの下位6bitを0マスクしたものがSYS命令と等しくなければiに077(nosys)を格納
      • 2761 : callpに正しいsysentをセット
      • 2762-2763 : u.u_arg[i]にシステムコール命令の引数を格納していく。callp->countは引数の数をあらわす
    • 2764-2768 : 間接システムコールでなければ
    • 2770 : システムコールへの引数の先頭をu.u_dirpに格納しておく。u.u_dirpはシステムコールハンドラ内で使われる
    • 2771 : trap1(2841)を呼び出す


trap1では、実際にシステムコールハンドラを呼び出します。

    • 2845 : u.u_intflgをセット
    • 2846 : r5, r6をu.u_qsavに保存しておく
    • 2847 : システムコールハ処理ルーチンを実行
    • 2848 : u.u_intflgをリセット


システムコールハンドラ内でシグナルが発生すると、2846で保存したu_qsavを復帰させてtrap1の呼び出し元へ戻ります。(sleep内の2106参照)

なので2848のu.u_intflgリセット文が実行されないのでセットされたままになります。

(このあたりの自分の認識が怪しい。要確認)

    • 2772-2773 : u.u_intflgがセットされていたらu.u_errorにEINTRをセット
    • 2774 : u.u_errorが100未満ならば。466-497を見ると「EFAULTでなければ」という意味な気がする。
      • 2775-2778 : u.u_errorがセットされていたらトラップを引き起こしたプロセスのPS[0]を1にセット。r0にu.u_errorをセット。このr0はどこで使用する?
      • 2779 : outへ飛び、シグナル処理とプロセス優先度の再計算を行ってtrapルーチン終了
    • 2781 : ここからはバスエラーと同じ処理が続く

その他メモ

  • ユーザープロセスで浮動小数点例外が起きたときに、すぐにシグナル処理をするのはなぜ? 別のユーザープロセスの浮動小数点操作による例外かもしれなのに
  • trapの最後にプロセス優先度を再計算するのはなぜ? 優先度が大きく変わりうるから?
  • 命令の下位6bitがsysent[n]のエントリに対応しているっていうのはつまりどういうこと?

終わりに

今回はコードをただ追っただけで、あまりOSの処理イメージがつかめていません。トラップやシステムコール周りの理解が進んだ後に色々補足するかもしれません。

次回は12章の続きでシステムコールハンドラの一部を追っていきます。内容が結構重そうです。