2011年2月13日日曜日

第9回 V7から始めるUNIX講座 復習とまとめ(割り込みPart1)

第9回 割り込み

●前提知識

・メモリ上に命令がリニアに並んでいる。
・PC(プログラムカウンタ)に次に実行する命令のアドレスが書いてある。
PCについては、以下を参照して下さい。
http://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%82%AB%E3%82%A6%E3%83%B3%E3%82%BF#.E3.83.97.E3.83.AD.E3.82.B0.E3.83.A9.E3.83.A0.E3.82.AB.E3.82.A6.E3.83.B3.E3.82.BF

命令の実行は以下の3サイクルで行われる。
①フェッチ(読み込み)
②解析(デコード)
③実行

詳細は以下の「動作」を参照して下さい。
http://ja.wikipedia.org/wiki/CPU
上記より引用
プログラムは数値列として何らかのメモリに格納されている。CPUでは、フェッチ、デコード、実行という3つのステップがほぼ必ず存在する。
最初の段階であるフェッチとは、実行すべき命令(ある数値または数値の並び)をプログラムの置かれたメモリから取り出すことである。メモリ上の実行すべき命令の位置はプログラムカウンタで指定される。プログラムカウンタはCPUが現在見ているプログラム上の位置を示しているとも言える。命令フェッチに使用されると、プログラムカウンタはフェッチしたぶんだけ増加させられる。

①フェッチが終わると
PCの内容は
PC=PC+長さ(命令)となって次の命令のアドレスを指します。

(備考)
RISC:命令の長さが固定
CISC:命令の長さが可変長

分岐処理
上記だと順番に命令を実行するだけです。条件分岐したい時は、PCを書き換えます。
絶対アドレス(例:PC=xxxxxxx)
相対ブランチ(例:PC=PC+3)

●割り込み

・割り込みの例
0除算
0除算したら続行できないので、0除算したら実行するサブルーチンを用意して、そのアドレスを持っておきます。
0除算が発生したら、そのアドレスにPCを書き換えます。
⇒0除算用のサブルーチンへ分岐する。

・タイマー
あるプロセスが無限ループしているような場合でも、他のプロセスは実行できます。
ある時間経ったら、強制的にカーネルモードに戻すからです。これも割り込みです。
スケジューラが他に優先度が高いプロセスがそちらに切り替えます。
変な無限ループプロセスは優先度を最低にしたりして、調整します。
⇒スケジューラの役割

・システムコール
システムコールも割り込みを使っています。
例えばgetpid

カーネルの機能を使ってPIDを取得するわけですが、カーネルの機能を勝手にユーザから呼ばれると困ります。
なので、ユーザ側から使えるカーネルの機能を制限をしています。

●UNIX関数のライブラリとシステムコールの違い
システムコールはカーネルの機能を使います。
ライブラリは
各種処理を行うが、システムコールを呼び出さないもの
各種処理を行い、最終的にはシステムコールを呼び出し、カーネルの機能を使うもの
に分かれます。

詳細は以下を参照
http://x68000.q-e-d.net/~68user/unix/func.html

システムコールについては以下が詳しいです。
http://ja.wikipedia.org/wiki/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%B3%E3%83%BC%E3%83%AB

●システムコールの呼び出し方
以下、getpidを例に流れを説明します。

まず、システムコールに対して番号を振っておきます。
例:getpidなら20とか

ライブラリのgetpidを見てみます。
いつもUSTで見てるいるサイトはカーネルのみです。
それ以外のソースは以下のサイトで公開されています。
http://minnie.tuhs.org/cgi-bin/utree.pl

getpidのソースファイルは以下
http://minnie.tuhs.org/cgi-bin/utree.pl?file=V7/usr/src/libc/sys/getpid.s


/ getpid -- get process ID

.globl _getpid
.getpid = 20.

_getpid:
mov r5,-(sp)
mov sp,r5
sys .getpid
mov (sp)+,r5
rts pc


sys .getpidを実行すると割り込みが起きます。

カーネルモードに移ってカーネルのgetpid()を実行します。

●ベクターテーブル
割り込みが発生した場合の対処窓口がベクターテーブルになります。

l.s(本来はlow.s)を見てください。
http://tamacom.com/tour/kernel/unix/S/7.html

これがベクターテーブルです。


15 / trap vectors
16 trap; br7+0. / bus error
17 trap; br7+1. / illegal instruction
18 trap; br7+2. / bpt-trace trap
19 trap; br7+3. / iot trap
20 trap; br7+4. / power fail
21 trap; br7+5. / emulator trap
22 start;br7+6. / system (overlaid by 'trap')


CPUは、PC(プログラムカウンタ)とStatus(特権状態かどうか、比較命令の結果ビット)を持っています。
CPUによっては呼び方が異なります。
PDP-11ではStatusは、PSW(Processor Status Word)と呼びます。
PSWは16ビットでそれぞれのビットには以下の意味があります。Lions本、P259より転載
14,15 現モード(00:カーネル、11:ユーザ)
12,13 前モード(00:カーネル、11:ユーザ)
5,6,7 プロセッサの優先度
4 トラップビット
3 N 直前の結果が負であった場合にセットされる
2 Z 直前の結果がゼロであった場合にセットされる
1 V 直前の操作がオーバーフローを示した場合にセットされる
0 C 直前の操作がキャリーを示した場合にセットされる。

ベクターテーブルは、それぞれの例外において、PCとPSWを並べたものです。
つまり、それぞれの例外において、PC(どこへ分岐して処理するか)とPSW(どういう状態で実行するか)を並べたものです。

もう一度ベクターテーブルを見てみます。
PSWがbr7+6になっています。
br7 = 340(8進数)なので、346で二進数にすると
11100110
を設定しています。

PSWの下位3ビットをどんな割り込みが発生したかを判定するために使っています。(後述)
システムコールの場合は、6を設定しています。
11100110

●疑問
PSWは16ビットですが、上記だと下位の8ビットしか設定していないように見えますが?
上位8ビットはCPU側で設定するのでしょうか?

●システムコールが発生したことの判定
PCにあたる部分が、trapになっています。
trapを実行するということです。
m40.sを見てみましょう。アセンブラのルーチンです。
http://tamacom.com/tour/kernel/unix/S/1.html


93 trap:
94 mov PS,saveps
95 tst nofault
96 bne 1f
97 mov SSR0,ssr
98 mov SSR2,ssr+4
99 mov $1,SSR0
100 jsr r0,call1; jmp _trap
101 / no return


100行目 jsr r0,call1; jmp _trap
設定を行ってから、_trap、つまりC言語のtrapへ飛びます。

C言語のtrapは、
http://tamacom.com/tour/kernel/unix/S/104.html#L34

switch文で分岐します。
49 switch(minor(dev)) {
minor(dev)
で割り込みが発生した原因を特定できます。

システムコールの場合
100 case 6+USER: /* sys call */

Case文の6は、ベクターテーブルで指定した6です。
PSWの下位を使ってないところを、割り込み原因のコードとして使ってます。
22 start;br7+6. / system (overlaid by 'trap')
 
+USERはユーザモードで発生した。
何もなければカーネルモードで発生した。
ということを示します。
システムコールはユーザ側で呼び出しますから、+USERになります。

●どのシステムコールを実行するかの判定
ここまでで、割り込みが発生し、それがシステムコールであることが分かりました。
どのシステムコールを実行するのかの判定です。
最初の方にシステムコールに番号を振っていると書きました。
sysを発行した場合にその番号を指定しています。
sys .getpid
.getpidは20

fuiwordでシステムコールを呼び出したときに指定した番号を取り出します。(sys .getpid)
104 callp = &sysent[fuiword((caddr_t)(a-1))&077];
fuiwordは以下にあります。
http://tamacom.com/tour/kernel/unix/S/1.html


615 _fuiword:
616 _fuword:
617 mov 2(sp),r1
618 fuword:
619 jsr pc,gword
620 rts pc
621
622 gword:
623 mov PS,-(sp)
624 bis $HIPRI,PS
625 mov nofault,-(sp)
626 mov $err,nofault
627 mfpi (r1)
628 mov (sp)+,r0
629 br 1f


mfpiで番号を取得しています。
LIONS本のP262に
mfpi 前のアドレス空間中の指定ワードの値をカレントスタックにプッシュする。
とあります。
前のアドレス空間、つまりユーザモードから、指定した番号(この場合は20)を取り出してカレントスタックに置きます。

その番号を元にして、sysentから実際に実行するシステムコールを決定します。
http://tamacom.com/tour/kernel/unix/S/102.html#L65

87 0, 0, getpid, /* 20 = getpid */

これでようやく、番号からカーネルモードのgetpid まで辿り着きました。

getpid を実行して値を返します。

●疑問点

大まかな流れは分かったのですが、いくつか疑問が

1、結果のリターン
プロセスIDを返す場合は、どうやってユーザモードのプロセスまで戻るのでしょう?

2、システムコールのパラメータ
getpid はパラメータがありませんでしたが、パラメータがある、例:open()の場合は、システムコールを識別する番号とパラメータをカーネルに渡す必要があると思いますが、どう渡すのでしょう?

パラメータの取り出しは以下でやっているような気がします。
120 for(; isy_narg; i++)
121 u.u_arg[i] = (*fetch)((caddr_t)a++);

また、取り出したパラメータを使っての実行がソースを見ても不明でした。

3、C言語の呼び出し
アセンブラのtrapからC言語のtrap(_trap)を呼んでいるようです。
C言語のtrapはパラメータが7つありますが、これはどう設定しているのでしょう?

34 trap(dev, sp, r1, nps, r0, pc, ps)

4、システムコールを発生させるために
22 start;br7+6. / system (overlaid by 'trap')
ベクターテーブルでシステムコールは上記のように記載しています。
sysで意図的に割り込みを発生させているわけですが、sysを発行した際に上記のベクターテーブルが使われるように何らかの設定(レジスタあるいはPSW?)をしているのでしょうか?

0 件のコメント:

コメントを投稿