LoginSignup
5
1

More than 5 years have passed since last update.

セマフォの実装について調べた

Posted at

詳解Linuxカーネルの5章カーネルの同期処理に関連する、セマフォについて調べた。

カーネルバージョン

v4.4

セマフォとは

ロックが取れない場合に、休止状態で実行を待たせる機能。スピンロックよりはCPUを有効活用出来るパターンもあるんだろうと思う。
いろいろ調べた後にに気づいたが、この機構はほとんど使われていない・・・古い機能なのかな?

構造体

解放と取得の関数はスピンロックで保護されている。いろんな関数がラップしているが__up()__down_common()が操作する関数の本体になる。
wait_listにはstruct semaphore_waiterが繋がっていて、task_structが辿れるようになっている。

/* Please don't access any members of this structure directly */
struct semaphore {
    raw_spinlock_t      lock;
    unsigned int        count;
    struct list_head    wait_list;
};

初期化論理

nは同時に取れるセマフォの数、DEFINE_SEMAPHOREマクロでラップされていて、その場合nは1になる。

#define __SEMAPHORE_INITIALIZER(name, n)                \
{                                   \
    .lock       = __RAW_SPIN_LOCK_UNLOCKED((name).lock),    \
    .count      = n,                        \
    .wait_list  = LIST_HEAD_INIT((name).wait_list),     \
}

セマフォの解放

__up()ではwait_listにつながって待っている最初のプロセスを起床させるだけで、特に難しいことはない。
countはゼロになるまではデクリメントするだけで良い。ここはスピンロックをとっているからアトミック操作になっている。

/**
 * up - release the semaphore
 * @sem: the semaphore to release
 *
 * Release the semaphore.  Unlike mutexes, up() may be called from any
 * context and even by tasks which have never called down().
 */
void up(struct semaphore *sem)
{
    unsigned long flags;

    raw_spin_lock_irqsave(&sem->lock, flags);
    if (likely(list_empty(&sem->wait_list)))
        sem->count++;
    else
        __up(sem);
    raw_spin_unlock_irqrestore(&sem->lock, flags);
}
EXPORT_SYMBOL(up);

セマフォの取得

down()はdeprecatedで、down_interruptible()か、down_killable()を使えとなっている。それぞれの違いはなんのシグナルに反応するかで、全部のシグナルか、SIGKILLだけかを選べる。

結局のところ、どの関数も__down_common()の内部で、次のforループで自分がupされるまでまつ。timeoutか指定のシグナルを受け取った場合は、ロックを取らずに返る。
スピンロックはschedule_timeout()の前で解除しているので、スピンロックをとりっぱなしにはならない。

    for (;;) {
        if (signal_pending_state(state, task))
            goto interrupted;
        if (unlikely(timeout <= 0))
            goto timed_out;
        __set_task_state(task, state);
        raw_spin_unlock_irq(&sem->lock);
        timeout = schedule_timeout(timeout);
        raw_spin_lock_irq(&sem->lock);
        if (waiter.up)
            return 0;
    }
5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1