読者です 読者をやめる 読者になる 読者になる

minix smpのロック実装

minix

毎度ながら、minix2smpのソースはここ。
http://gsd.unex.es/projects/minixsmp/

このソースではロックはlock_mp_kernel()、mp_switching_lock()、mp_heldq_lock ()などの関数がほぼ行っていて、
#smpの実装で追加された部分に関してはそうだと思う。
これらはアセンブラのコードなんだけど、ほんとの実態は下のマクロを呼び出すだけです。
違いは、マクロに渡す引数の変数が違うくらい。

#define MP_LOCK_FREEVAL         0 

#define MP_LOCK(semaphore_var)                          ;\
0:              clc                                     ;\
        lock    bts     (semaphore_var),        0       ;\
                jnc     2f                              ;\
1:              cmp     (semaphore_var),MP_LOCK_FREEVAL ;\
                jne     1b                              ;\
                jmp     0b                              ;\
2:   

これはどんなことをしているかというと、
A.cls命令でCFフラグ(キャリーフラグ)をクリア
B.bts命令でsemaphore_varの値の0ビット目をCFフラグに入れるのと0ビット目を1にする
 lockプリフェックスが付いてるので、バスロックしたうえでbts命令が実行される
C.CFフラグが0ならラベル2に飛ぶ(ロックの処理終了)
D.cmp命令でsemaphore_varとMP_LOCK_FREEVAL(値は0)を比較
 cmp命令の結果で比較結果が同一と判断されたら、ゼロフラグが立つになる
E.ゼロフラグの値が0なら、ラベル1に飛ぶ
 cmp命令の結果はゼロフラグにあるので、jne命令はそれを見る
F.ラベル0に飛ぶ

jmpとかjne命令に使ってる1bとか2fのbやfの意味は、数字はラベルを表してて、
後ろに付いているアルファベットはfはforward、bはbackwardの意味なので、
今実行している命令の前に出てきたラベル1とか、今実行している命令より先にあるラベル2と言う意味です。

ということで、ロックの実装は割と単純な感じで、
例えば、lock_mp_kernel()の場合で行くと・・・
#この関数は_k_main_lockという変数をマクロに渡します。

誰もロックをしていない場合:、
Bのところでbts命令でロックが取得済みかどうか調べて、
ロックされてない場合(CFフラグが0なら)はそのまま処理を終了する(bts命令によって、0ビット目が1になってます)

誰かがロックをしている場合:
ロックを開放する前に誰かがlock_mp_kernel()を呼んだ場合、bts命令後のCFフラグの値は1になり既に誰かがロックしていることがわかります。
当然bts命令後に0ビット目が1に変更されるんだけど、元々このビットは1になっていたから何の問題も起きないわけです。バスロックもされているのでbts命令実行中に誰かがこのビットを0に変えられないはずなので。
そんで、次のcmp命令でマクロで渡ってきた変数の値が0になるまでcmp命令を繰り返し続ける。
cmp命令でsemaphore_varの値が0になったことが確認できたら、ラベル0に飛んで、ロックを取得できるかbts命令を試す。
これで、ロックが取得できればめでたくロック取得処理を終了で切るけど、
できなければ、ロックが取得できるまで延々とループを行うことになる。