UNIX 6th code reading - 割り込み・トラップルーチンその2

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

はじめに

今回は10章に出てくるコードの詳細を追っていきます。

fuiword

まずは通常動作を見ていきます。

通常動作
  • 845 : この時点でスタックの先頭にはfuiwordを呼んだ命令(の次の命令)へのリターンアドレス、fuiwordに対する引数が積んであります。
sp stack
 
-> PC(return to fuiword caller)
arg for fuiword
 
  • 846 : fuiwordに対する引数をr1へ保存
  • 848 : PC(return address)をスタックに積んでgwordへジャンプ


line#はunix v6の行番号を表します。

sp stack register line#
 
-> PC(return to 849) 848
PC(return to fuiword caller)
arg for fuiword -> r1 846
 
  • 852 : PSをスタックに積む
  • 853 : PSのプロセッサ優先度を7にして割り込みを防ぐ
  • 854 : nofaultをスタックに積む
  • 855 : nofaultにerr(trap handler)を設定
sp stack register line#
 
-> nofault 854
PS 852
PC(return to 849)
PC(return to fuiword caller)
arg for fuiword
 
variable value line#
nofault <- address to err 855
  • 856-857 : r1を引数にmfpiを実行。結果はスタックに積まれる。その結果をr0に移す
  • 876 : スタックに積んでいたnofaultの元の値を復帰
  • 877 : スタックに積んでいたPSを復帰(プロセッサ優先度も元に戻る=割り込み可)
  • 878 : スタックの先頭に積んであるPC(return to 849)を用いてfwordに戻る
  • 849 : スタックの先頭に積んであるPCを用いてfuiwordの呼び出し元へ戻る
sp stack register line#
 
result of mfpi(r1) -> r0 856, 857
nofault -> nofault 876
PS -> PS 877
PC(return to 849) 878
PC(return to fuiword caller) 849
-> arg for fuiword
 
variable value line#
nofault <- address to err 855
mfpi(856)でバスタイムエラーが起きた場合

次はmfpi(856)でバスタイムエラーが起きた場合を見ていきます。

mfpi(856)が実行される時点のスタック、nofaultは以下の状態です。

sp stack register line#
 
-> nofault(original)
PS
PC(return to 849)
PC(return to fuiword caller)
arg for fuiword
 
variable value line#
nofault address to err
  • 856 : mfpiでバスタイムエラー。結果はスタックに積まれない(と思う)。(おそらくこのタイミングで)現在のPS, PCがスタックに積まれる。PCは512を差し512へジャンプ
  • 512 : この文はよくわからない。ここで現在のPC, PSがvector addressから設定される? 現モード、前モードはカーネルモードになる。fuiwordはカーネルモードで実行されるので、モードは変わらない。使用するスタックもカーネルスタックのまま変わらない。trap(755)へジャンプ
sp stack register line#
 
-> PC(return to gword(857?)) 856 ?
PS 856 ?
nofault(original)
PS
PC(return to 849)
PC(return to fuiword caller)
arg for fuiword
 
variable value line#
nofault address to err
  • 756 : PSをスタックの先頭の2word前へ積む。何のためかはよくわからない
  • 757, 758 : nofaultが偽か(?)調べる。errへのアドレスが入っているので764へ分岐
  • 766 : SSRを初期化してメモリ管理を有効に。なんのため?
  • 767 : スタックの先頭に積んであるgwordへのリターンアドレスをnofault(errのアドレス)に置き換える
  • 768 : スタックの先頭からPC, PSを復帰。PCにはerrが入るのでerr(880)へジャンプ
sp stack register line#
 
PS
 
address to err -> PC 767, 768
PS -> PS 768
-> nofault(original)
PS
PC(return to 849)
PC(return to fuiword caller)
arg for fuiword
 
variable value line#
nofault address to err
  • 881 : スタックに積んでおいたnofaultの元の値を復帰
  • 882 : スタックに積んでおいたPSの元の値を復帰
  • 883 : fuword(849)へのリターンアドレスをスキップ。なぜtst命令? つまりfuiwordへは戻らない
  • 884 : mfpi命令の結果(=fuiwordの結果)が格納されるr0に-1を入れる。これがエラーを示す
  • 885 : fuiwordの呼び出し元へ戻る
sp stack register line#
 
nofault(original) -> nofault 881
PS -> PS 882
PC(return to 849) 883
PC(return to fuiword caller) 885
-> arg for fuiword
 
register value line#
r0 -1 884

トラップハンドラ(err)がやっている処理は、トラップの共通処理を除けば「884のr0に-1を入れる」だけと言えるかもしれません。

クロック割り込み

クロック割り込みが発生するとvector addressからPS, PCが設定される。ユーザーモードのプロセスに対して割り込んでいる可能性もあるので、スタックによるデータの受け渡しは使えない。PCは570を指す。

クロックに割り込まれた時のカーネルスタックの状態。割り込まれたプロセスのPC, PSが積まれている。

sp stack register line#
 
-> PC(return to the interrupted process) 570?
PS(of the interrupted process) 570?
 
  • 570 : jsr命令により、r0をスタックに積み、PCにはcallのアドレス(776)が入る。また、r0には_clockのアドレスが入る
sp stack register line#
 
-> r0 570
PC(return to the interrupted process)
PS(of the interrupted process)
 
register value line#
r0 <- address to _clock(3725) 570
  • 777 : PSをスタックに積む
  • 779 : r1をスタックに積む。(r1には何が入っている?)
  • 780 : 割り込まれたプロセスのspを積む?
sp stack register line#
 
-> sp(of the interrupted process) 780
r1 779
PS 777
r0 570
PC(return to the interrupted process)
PS(of the interrupted process)
 
register value line#
r0 address to _clock(3725)
  • 781 : 777でスタックに積んだPSをさらにスタックに積む
  • 782 : 先頭にあるPSの下位5bitを除いてマスク(八進数の37は二進数で11111)
  • 783 : 前モード(割り込まれたプロセスのモード)をチェック(PSの[13:12])
sp stack register line#
 
-> PS(masked) 781, 782
sp(of the interrupted process)
r1
PS
r0
PC(return to the interrupted process)
PS(of the interrupted process)
 
register value line#
r0 address to _clock(3725)

ここから前モードがカーネルモードかユーザーモードかで分岐します。

前モードがユーザーモードだった場合
  • 784 : 797へ分岐
  • 798 : 前モードをユーザーモードに設定(なぜ?)
  • 799 : r0(_clock(3725))へジャンプ。スタックの先頭にPCを積む
sp stack register line#
 
-> PC(return to _call(800) 799
PS(masked)
sp(of the interrupted process)
r1
PS
r0
PC(return to the interrupted process)
PS(of the interrupted process)
 
register value line#
r0 address to _clock(3725)

_clock(3725)は次章で扱います。_clockから戻ってきた後の処理を追います。

  • 800 : スタックの先頭から二つ(PS(masked)とsp(of the interrupted process))を取り除きます。
  • 802 : スタックに積んでおいたr1を復帰
  • 803 : スタックに積んでおいたPSをスキップ
  • 804 : スタックに積んでおいたr0を復帰
  • 805 : rtt命令で割り込まれたプロセスに戻る
sp stack register line#
 
-> PC(return to _call(800)
PS(masked) 800
sp(of the interrupted process) 800
r1 -> r1 802
PS 803
r0 -> r0 804
PC(return to the interrupted process) -> PC 805
PS(of the interrupted process) -> PS 805
 
前モードがユーザーモードだった場合
  • 784 : 前モードがユーザーモードなので分岐しない
  • 785 : r0(_clock(3725))へジャンプ。スタックの先頭にPCを積む
sp stack register line#
 
-> PC(return to _call(786) 785
PS(masked)
sp(of the interrupted process)
r1
PS
r0
PC(return to the interrupted process)
PS(of the interrupted process)
 
register value line#
r0 address to _clock(3725)

上記のスタックの状態はLions本344Pのものです。(スタックの先頭方向が逆なので注意)

Lions本でも書かれている通り、Cの関数内でcsv(1420)が呼び出されてr2-5がスタックに積まれます。csvについては以前のエントリでも少し触れています。

clockから戻ってきた後の処理を追います。

  • 787 : プロセッサ優先度(PS[7:5])を7にして割り込みを防ぐ
  • 788 : runrunフラグがセットされているかチェック
sp stack register line#
 
PC(return to _call(786))
-> PS(masked)
sp(of the interrupted process)
r1
PS
r0
PC(return to the interrupted process)
PS(of the interrupted process)
 

まずはrunrunフラグがセットされていない場合を追います。

  • 789 : セットされていなければ793へ
  • 794 : スタックに積んでおいたmasked PSをスキップ
  • 795 : スタックに積んでおいたspを割り込まれたプロセスのspに移す?
sp stack register line#
 
PS(masked) 794
sp(of the interrupted process) 795
-> r1
PS
r0
PC(return to the interrupted process)
PS(of the interrupted process)
 

ここからは前モードがカーネルモードのときの場合と同じです。

続いてrunrunフラグがセットされていた場合の処理を追います。

  • 789 : セットされていないので分岐しない
  • 790 : プロセッサ優先度を0に戻す
  • 791 : swtchを呼び出して実行プロセスの切り替え。(最も優先度の高いプロセスが選択される)
sp stack register line#
 
-> PC(return to 792)
PS(masked)
sp(of the interrupted process)
r1
PS
r0
PC(return to the interrupted process)
PS(of the interrupted process)
 

このプロセスが再活性したときは796 -> 786と移り、runrunフラグのチェックを再度行う。

ユーザープログラムのトラップ(システムコール)

現モードはカーネルモードになる。システムコールはユーザープログラム(ユーザーモードで実行されているプロセス)から呼び出さえれるので、前モードはユーザーモード。なので、スタックによるデータの受け渡しは使えない。PCは570を指す。

システムコールが呼び出された時のカーネルスタックの状態。ユーザープログラムのPC, PSが積まれている。

sp stack register line#
 
-> PC(return to the interrupted process) 570?
PS(of the interrupted process) 570?
 
  • 518 : vector addressからPC, PSをロード。755へジャンプ
  • 756 : PSをスタックの先頭から2word前にコピー。PSにはトラップ種別情報を含んでいるので、すぐに保存する必要がある
  • 757 : nofaultをチェック
  • 758 : nofaultには何も入っていないので分岐しない
  • 759-761 : SSRの設定(なぜ必要?)
  • 762 : jsr命令でcall1(771)へジャンプ
  • 772 : spを756で積んだPSを指すようにする
  • 773 : プロセッサ優先度を0に設定(割り込みok)
sp stack register line#
 
-> PS(includes trap kind) 756, 772
r0 762
PC(return to the interrupted process)
PS(of the interrupted process)
 
register value line#
r0 address to _trap(2693) 762

ここから先は割り込み処理と同じです。785でr0(_trap)にジャンプします。前モードは必ずユーザーモードなので798-のパスは通りません。

終わりに

コードのメモだけではわかりづらいと思いスタックの状態も併せて書いてみました。いかがでしょうか。結構大変だったので次からはやらないかもしれません笑

次は11章。クロック割り込み処理を追っていきます。