無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

スピンロック


スピンロックの取得はspin_lockマクロで定義されています。preempt_disable()でカレントプロセスのpreempt_countをインクリメントします。preempt_count=0の時プリエンプトされます。_raw_spin_trylock()でロックが取得しにいきます。取得できなければ、__preempt_spin_loc()でビジーウエイトとよばれるループします。
#define spin_lock(lock) \
do { \
       preempt_disable(); \
       if (unlikely(!_raw_spin_trylock(lock))) \
               __preempt_spin_lock(lock); \
} while (0)
_raw_spin_trylock()は、lock->lockとoldval=0(oldvalはeax,ebx,ecx,edxの中のレジスタ。)を、xchgbで交換します。もし、movコマンドでこの操作をおこなうと、変換途中で他のCPUで、そのlock->lockが更新されることがありますが、xchgbはアトミックで行われ、本変換中は、他のCPUからlock->lockをアクセスできません。

その結果oldval(lock->lockの中身)が0でないなら、ロックが取得できたことになり、同時にlock->lockには0が設定され、ロック状態を設定したことになります。ロックできないという事は、lock->lock=0で、oldval=0と変換しても、ロック状態のlock->lock=0のままです。
static inline int _raw_spin_trylock(spinlock_t *lock)
{
       char oldval;
       __asm__ __volatile__(
               "xchgb %b0,%1"
               :"=q" (oldval), "=m" (lock->lock)
               :"0" (0) : "memory");
       return oldval > 0;
}
__preempt_spin_lock()がビジーウエイトと呼ばれる処理になり、プリエンプトが可能かどうかによってその処理は、異なっています。

プリエンプトが不可なら、_raw_spin_lock()が、可能ならpreempt_enable()で再スケジュールしてロックを取得しに行きます。プリエンプトが不可と言うことは、他のCPU下のプロセスがロックを取得していると言う事(たぶん)。プリエンプトが可と言うのは、このCPU下の他のプロセスがロックを取得している場合もある。ということ(たぶん)。
void __preempt_spin_lock(spinlock_t *lock)
{
       if (preempt_count() > 1) {
               _raw_spin_lock(lock);
               return;
       }
       do {
               preempt_enable();
               while (spin_is_locked(lock))
                       cpu_relax();
               preempt_disable();
       } while (!_raw_spin_trylock(lock));
}

#define spin_is_locked(x)       (*(volatile signed char *)(&(x)->lock) <= 0)
_raw_spin_lock()は、アセンブラのlock命令を介して、lock->lockをデクリメントして、それが0になるまでループし続けます。
static inline void _raw_spin_lock(spinlock_t *lock)
{
       __asm__ __volatile__(
               spin_lock_string
               :"=m" (lock->lock) : : "memory");
}

#define spin_lock_string \
       "\n1:\t" \
       "lock ; decb %0\n\t" \        <- lock->lockのデクリメント
       "js 2f\n" \                   <- ロックされていたら2:へジャンプ
       LOCK_SECTION_START("") \      <- ロックできたらこの処理がされる。
       "2:\t" \
       "rep;nop\n\t" \
       "cmpb $0,%0\n\t" \      <- lock->lockが0かチェック
       "jle 2b\n\t" \                <- 0より小さいと2:へジャンプ(ビジーウエイト)
       "jmp 1b\n" \         <- 1:へジャンプして、再度ロック取得
プリエンプトが可の場合、このCPU下のプロセスがロックを取得している可能性があるため、preempt_enable()で再シュケジュールを行います。(ここでのpreempt_enable()はプリエンプトを可能にするためでなく、再シュケジュールのためにコールします。)そして、ロックを取得できるまでループします(取得はしません。チェックだけです。)

取得可能なら、preempt_disable()で、先のpreempt_enable()でインクリメントした、preempt_countを元に戻します。そして_raw_spin_trylock()でロック取得を試みます。ロック取得できる状態になってから、実際ロック取得するまでに、他のプロセスがロックを取得している場合があるからです。

追記

要はスピンロックは、他のCPUからも排他的にアクセスできるフラグと言う事なのですが、プリエンプトとの兼ね合いでその実装は今ひとつしっくりきません。


最終更新 2013/04/17 18:40:06 - north
(2013/04/17 18:25:57 作成)