Lions’ Commentary on UNIX 読書会メモ#5

はじめに

Lions本読書会#5に参加してきました。

そのときのメモを残しておきます。

今回は12章「トラップとシステムコール」を読みました。

当初は13章も読む予定だったのですが、12章があまりにボリュームがあったため12章だけを詳細に追うことになりました。

個人的な印象ですが、今までのLions本読書会の中で一番討論が活発になされた気がします。

(ホワイトボードの写真を撮っておけばよかった……)

togetter


togetterを使って、関連ツイートをまとめました。

当日の雰囲気がつかめると思います。

自分の発言が多く、また、文や表現のおかしなものが多いですが気にされぬよう。

メモ

trap.c
  • estabur( )(1650)は物理←→仮想アドレスマッピングを司るレジスタの調整。内部でsureg( )(1739)も呼んでいるので、レジスタは更新され設定が即座に反映される。7章の323P参照
    • プロセスのテキスト、データ、スタックセグメントサイズの調整後に呼び出されることが多い
  • カーネルモードでトラップがあまり使われないのってなんでだっけ?
    • 10章の340Pあたりを参照。コードならば855行あたり。nofaultを設定して、そいつに処理をさせるのが基本? なのでtrap.cによる処理は通常想定していない?
    • カーネルならば「例外が起こる処理をこれから行う」ことが事前にわかっているので、例外動作が起きたときの処理を予め指定できる(nofault設定により)
    • カーネルならばトラップなんて機構を使わなくても色々いじれるし
  • trap( )のswtchでcase 8:の場合(カーネルモードで、ユーザモードで実行した浮動小数点命令による例外をトラップした場合。浮動小数点命令は処理に数サイクル必要なので、カーネルモード遷移前にユーザモードで浮動小数点命令を実行した場合にこういうケースがあり得る。カーネルモードでは通常浮動小数点命令は使用しない)
    • 他のcase文のようにbreakするのではなく、returnですぐに戻っているのはスタックの復帰ができなくなるため?(ここ後日フォローするかも)
  • trap( )のswtchでcase 9+USER:のセグメンテーション例外について。データセグメントが足りなくなったらgrowで拡張する。成功したらreturn。失敗したらソフトウェア割り込みを行う
    • ユーザ(人)レベルでは見えないところで、内部でtrapが起きていると想像すると面白い
  • system call handlerを呼び出すtrap1( )にて、handler呼び出す前にsavu(u.u_qsav)を呼び出しているのは何のため?
    • sleep( )(2066)にて、sleep( )第二引数が0以上の場合にのみissig( )でシグナルチェックをし、シグナルを受け取っていたらretu(u.u_qsav)によるr5, r6復帰をしている。復帰後はその後のreturnでtrap1( )を呼び出した命令(の次の命令)に戻るはず
    • sleepシステムコール(ssleep( ), 5979)のために設定している? ssleep( )から呼び出すsleep( )(5994)の第二引数はPSLEP(=90)で0以上
    • 他のsystem call handler内でもsleep( )を呼ぶ箇所はある。しかし、今回見た範囲では第二引数は0未満になっている。やはりsavu(u.u_qsav)はssleep( )のため?
  • system call handlerの間接呼び出しはlibcのwriteが参考になる
exec
  • system call handlerに対する引数はどう渡している?
    • プロセス固有領域のu.u_argに詰め込んで渡している。2751行あたりを参照。
    • exec( )の実行ファイルはuchar( )を使って読んでいるが、uchar( )内部でu.u_dirpを使っている。u.u_dirpは2770行でu.u_arg[0]が入っている。なぜu.u_arg[0]ではなくu.u_dirp経由でアクセスするのだろう? nami( )(7518)の都合?
  • execntで寝るプロセス(3038)は、第二引数が負。つまりソフトウェア割り込みを無視する
    • killで殺せない? kill( )(3630)を見ても、すぐにソフトウェア割り込みを処理するようにはなっていない。psignal( )を読んでシグナルをセットしているだけ
    • たくさんexec( )を実行してしまうとkill( )で殺せないので、どんどんプロセスがたまってしまう。あまりよろしくない設計?
  • a.outのヘッダはman5を参照
  • w0=410はテキストセグメントとデータセグメントの間にパディングが入って分けれている構成
  • 最近のプロセッサではIとDを分けていない
  • 3127行目のu.u_prof[3]=0の謎。(なんのために設定している?)
  • 3138行あたりのデータセグメント読み出しは、アドレス0にデータセグメントを読み出している模様。u.u_base = 0なので
  • 3149行目あたりのスタックセグメント初期化は、実行プログラムに対して引数をスタックに詰めている。この引数は3052行目あたりでu.u_argからバッファに詰め込んだ引数
    • 引数を詰め込んだ後のスタックの状態はman2のexec参考のこと
  • 3183行目あたりを見ると、u.u_signal[n]に偶数(ソフトウェア割り込みハンドラのアドレス)が設定されている場合0(強制終了)をセットしているように見える
    • 奇数(そのソフトウェア割り込みを無視する)設定は変更しない
    • execの前には通常forkが実行される。forkは親プロセスのコピーをしていると言っていい。つまり親プロセスのソフトウェア割り込み無視の設定が継承されているように見える
  • 3188行目。なぜr7だけfor文で回さずに別命令でリセット? pcだから?
fork
  • newproc( )を使って新しいプロセスを生成(コピー)している
    • 重要な点はfork( )を実行した親プロセスと、新しく生成された子プロセスの処理の流れ。親プロセスは3347行目のpcインクリメントにより、直後のbr(分岐命令)を飛び越える。以前のエントリ参考のこと
sbreak
  • なんでbreakなんていう名前?
    • man2のbreakを見ると、データセグメントの末端? スタックの先頭?がbreakと呼ばれているらしい。これを操作するのでbreakと呼ばれている?
    • man2より引用

Break sets the system’s idea of the lowest location not used by the program (called the break) to addr
(rounded up to the next multiple of 64 bytes).

  • 拡張したデータセグメントには、スタックセグメントの内容をコピーしているように見える。Lions本356Pも参考のこと

参考

他の参加者の読書会メモ。考察なども含まれていてさすがです。

終わりに

他にも色々話は出たのですが、細かい部分までは拾いきれませんでした。他の参加者のフォローを期待します。

次回は再来週の4/10(Sun)です。

実際のOSのコードを読んでみたい! という方は参加してみてはいかがでしょうか。