UNIX 7th code reading - process switch (context switch)
はじめに
前回のエントリで、UNIX 6thと7thでprocess switch(context switch)の仕様が違うかもしれないという話をしました。
今回はそれを明らかにするために、7thのsave( )とresume( )を確認していきます。
newproc( )で生成したプロセスがswtch( )で選択されたときの流れを見ていきます。
6thでは
6thについては以前解説を書いたので、そちらを参考にしてください。
7thでは
save
まずはnewproc( )の中でsave( )を呼んでいる箇所を確認します。
http://www.tamacom.com/tour/kernel/unix/S/96.html
503 if (save(u.u_ssav)) { 504 sureg(); 505 return(1); 506 }
u.u_ssavを引数にsave( )を呼んでいます。u_ssavの型はlabel_tです。label_tがどういうものかを確認すると、int6個分の配列であることがわかります。
http://www.tamacom.com/tour/kernel/unix/S/68.html
5 typedef int label_t[6]; /* program status */
save( )に制御が移ったときのスタックの状態は以下のようになります。なぜこうなるかは6thについてまとめたエントリを参考にしてください。7thでも話は変わっていないはずです。
sp | stack |
---|---|
sp -> | return address(PC) |
u.u_ssav(argument) | |
続いてsave( )の中のロジックを見ていきます。
http://www.tamacom.com/tour/kernel/unix/S/1.html
713 .globl _save 714 _save: 715 mov (sp)+,r1 716 mov (sp),r0 717 mov r2,(r0)+ 718 mov r3,(r0)+ 719 mov r4,(r0)+ 720 mov r5,(r0)+ 721 mov sp,(r0)+ 722 mov r1,(r0)+ 723 clr r0 724 jmp (r1)
最初の2命令でsaveから戻る先のアドレス(return address)をr1に格納し、u.u_ssavのポインタをr0に格納しています。
続いてr2からr6(sp)までをu.u_ssav配列に格納していき、最後尾にr1を格納しています。表で表すとこうなります。
index | u.u_ssav |
---|---|
0 | r2 |
1 | r3 |
2 | r4 |
3 | r5 |
4 | r6(sp) |
5 | r1(return address) |
その後r0をクリアし、jmp命令でr1(return address)に飛ぶことで、save( )呼び出し元に戻ります。r0が返り値となるので、呼び出し元には0が返ります。
呼び出し元であるnewproc( )を再確認します。
503 if (save(u.u_ssav)) { 504 sureg(); 505 return(1); 506 }
save( )から0が返ってくるので、if文の条件式は偽となり、507行目以降の処理を継続します。newproc( )を呼び出したのはプロセスを新規に作成しようとしている親プロセスですので、新規プロセスの初期設定を継続するというわけです。
resume
続いて、swtch( )で新たなプロセスが選択された後の処理を確認します。
http://www.tamacom.com/tour/kernel/unix/S/96.html#L347
416 n = p->p_flag&SSWAP; 417 p->p_flag &= ~SSWAP; 418 resume(p->p_addr, n? u.u_ssav: u.u_rsav);
returnではなく、resume( )を使っているのがわかります。newproc( )で生成されたプロセスにはSSWAPフラグが立つので
メモ:newproc( )で立てられたSSWAPフラグは上記417行目で落とされる?
528 rpp->p_flag |= SSWAP;
resume( )の第二引数にはu.u_ssavのポインタが渡ります。resume( )に制御が移ったときのスタックの状態は以下のようになります。
sp | stack |
---|---|
sp -> | return address(PC) |
p->p_addr(argument) | |
u.u_ssav(argument) | |
resume( )のロジックを確認していきます。
http://www.tamacom.com/tour/kernel/unix/S/1.html
726 .globl _resume 727 _resume: 728 mov 2(sp),r0 / new process 729 mov 4(sp),r1 / new stack 730 bis $HIPRI,PS 731 mov r0,KISA6 / In new process 732 mov (r1)+,r2 733 mov (r1)+,r3 734 mov (r1)+,r4 735 mov (r1)+,r5 736 mov (r1)+,sp 737 mov $1,r0 738 bic $HIPRI,PS 739 jmp *(r1)+
最初の2命令で、p->p_addrをr0に、u.u_ssavをr1に格納しています。
続いて割り込みを防ぐためにプロセッサ優先度を上げた後に、r0(p->p_addr)をKISA6(7番目のPAR)を格納します。この時点でu構造体が新たに選択されたプロセスの領域を指すようになります。このあたりは6thのときのまとめを参考にしてください。
u構造体が選択されたプロセスの領域を指すようになるということは、u.u_ssavを使ってsave( )で格納した情報を取得できるようになったということです。
save( )でs.u_ssavに格納したr2-6を復帰させます。r0に1を格納させた後にプロセッサ優先度を元に戻します。この時点でのu.u_ssav, registerの状態を表すと以下のようになります。
index | u.u_ssav | r | ||
---|---|---|---|---|
0 | r2 | -> | r2 | |
1 | r3 | -> | r3 | |
2 | r4 | -> | r4 | |
3 | r5 | -> | r5 | |
4 | r6 | -> | r6 | |
r1-> | 5 | r1(return address) |
r | value |
---|---|
r0 | 1 |
jmp命令でresume( )を抜けます。オペランドは*(r1)+です。この時点でr1はu.u_ssavの最後尾を指していて、そこにはsave( )を実行したときのreturn addressが格納されています。返り値であるr0には1が格納されているので、save( )を実行した箇所にreturn 1で戻るというわけです。
newproc( )のsave( )実行箇所を再度確認します。
503 if (save(u.u_ssav)) { 504 sureg(); 505 return(1); 506 }
return 1で戻ってくるので、if文の条件式が真となり、sureg( )を実行してPAR, PDRを設定しなおしてから、return 1でnewproc( )の呼び出し元に戻ります。
6thと7thの違い
7thではprocess switch(context switch)処理がだいぶシンプルになった感じがします。
6thではreturnの先を無理やり制御しようとしていたのに対し、7thではreturnの変わりにresume( )を使っているのが大きな違いです。
前回のエントリで載せた絵を再掲しておきます。
終わりに
気になっていた7thのprocess switch(context swtich)が確認できました。newproc( )でSSWAPする話やu.u_ssav, u.u_rsavの使い分けなど、細かな部分はまだ理解できていませんが、プロセス周りを確認するときに見ていこうと思います。
次からはファイルシステム周りを見ていく予定です。