排他制御関連
Rev.24を表示中。最新版はこちら。
BSD family 互換の割り込み禁止関数 [Legacy]
osfmk/kern/spl.h- splhigh() 全H/W割り込み禁止
- splsched() スケジューラのS/W割り込み禁止
- splclock() S/Wタイマ割り込み禁止
- splx(x) 指定割り込みレベルに設定
- spllo() 割り込み許可状態にする
ユニプロセッサのシステムでプロセスコンテキストと割り込みコンテキストの間で排他制御をするのに使われていた。
splhigh,sched,clock()は本当は各IPL(Ineterrupt Priority Level)に応じた割り込みを禁止するものだが、xnuではH/W割り込みを禁止にしているだけでどの関数も同じレベルとなる。これらは、ml_set_interrupts_enabled(FALSE)でH/W割り込みを禁止にしている(i386だとcliインストラクションで割り込み禁止)。
これらの関数は割り込みを禁止するだけなので、マルチプロセッサ環境においては役に立たない。Xnuのコード上には残っているが、新たなコードでは使わないこと(というか使っても意味がない)。排他制御を行う場合はSpinlockなどを使用すること。
splhigh,sched,clock()は本当は各IPL(Ineterrupt Priority Level)に応じた割り込みを禁止するものだが、xnuではH/W割り込みを禁止にしているだけでどの関数も同じレベルとなる。これらは、ml_set_interrupts_enabled(FALSE)でH/W割り込みを禁止にしている(i386だとcliインストラクションで割り込み禁止)。
これらの関数は割り込みを禁止するだけなので、マルチプロセッサ環境においては役に立たない。Xnuのコード上には残っているが、新たなコードでは使わないこと(というか使っても意味がない)。排他制御を行う場合はSpinlockなどを使用すること。
スピンロック
スピンロックはロックが取れるまでループ(スピン)を繰りかえして、プロセッサ間で排他制御を行なう。ロックが取られていた場合は、ロックが取れるまでループする。コンテキストがブロックしないのでCPU時間を無駄にしがち。割り込みハンドラの中などブロック不可能な時以外はmutexを使用するのがよい。osfmk/kern/simple_lock.h
simple_lock()
spin lock。ロックが取れるまでループする。
マクロで実体はusimple_lock()
usimple_lock()はhw_lock_to()でロックを取ろうとする。ロックがとれたらsimple_unlock()でロック解除するまでPreemption禁止になる。これは、ロックを取っている間に別プロセスに切り替わってしまうと、新プロセスが同じロックを取ろうとした場合にデッドロックが発生するため。また、同じ理由でロックを取っている最中はプロセスをブロックさせる処理はしてはいけない。
割り込み処理の中からもロックを取る処理がある場合は、割り込みも禁止にしないとデッドロックが発生するので、ロックを取りにいく前に、ml_set_interrupts_enabled(FALSE)してやる必要がある。Linuxでいうspin_lock_irqsave()相当(割り込み禁止にしてロックを取得)のものはないみたい。
RealTimeClockの処理(osfmk/i386/rtclock.c)では、ロック取得時にsplclock()で割り込みを禁止している(RTC_LOCKマクロ)。splxxはLegacyなのでml_set_interrupts_enabled()でやった方がよい?
mutex
スピンロックと異なりこちらはロックが取れなかった場合はプロセスがブロックする。再度動きだすのは、mutexをロックしていたプロセスがロックを解放した時。osfmk/i386/i386_lock.s
lck_mtx_lock
mutex_lockも同じだが、こちらはデバッグ用のチェック処理が入っている。
lck_mtx_unlock
ロックの解放
osfmk/kern/locks.c
lck_mtx_lock_wait()
ロックが取れなかった時にプロセスをブロックさせる。
lck_mtx_unlock_wakeup()lck_mtx_lock_wait()でブロックしているプロセスを起こす。
lck_mtx_unlock()でロックを解放する時に呼ばれる。
lck_mtx_unlock()でロックを解放する時に呼ばれる。