排他制御 (spin_lock, mutex_lock)

排他制御とは

 排他制御は、複数のプロセスやスレッドが共有リソースにアクセスする際に競合を防ぐ為の仕組みです。一方の処理が終わるまで、他の処理を止める事ができます。これによりデータの整合性等を維持できます。Linuxデバイスドライバで使用可能な主要な排他制御には、mutex_lockとspin_lockがあり、それぞれについて説明します。

mutex_lock の使い方

 mutex_lockは、ロックを取得できるまでスリープ状態に入るロックで、CPU負荷が少ない排他制御です。その為、長時間の排他制御に適しています。ただし、スリープ状態に入る為、オーバーヘッドが大きくなる場合があります。割り込み処理等、短時間で処理を実行する必要があるケースには不向きです。

照度センサデバイスドライバでは、以下に使用箇所があります。

kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/alsps.c

mutex_init(&obj->alsps_op_mutex);

このmutex_init関数は、mutexを初期化する為の関数で以下に定義されています。

kernel/mediatek/4.4/include/linux/mutex.h
kernel/mediatek/4.4/kernel/locking/mutex.c

/**
 * mutex_init - initialize the mutex
 * @mutex: the mutex to be initialized
 *
 * Initialize the mutex to unlocked state.
 *
 * It is not allowed to initialize an already locked mutex.
 */
# define mutex_init(mutex) \
do {							\
	static struct lock_class_key __key;		\
							\
	__mutex_init((mutex), #mutex, &__key);		\
} while (0)

引数には、struct mutex型のポインタを指定します。その他の、mutex_lockに関する主要な関数には、以下のようなものがあります。

/**
 * mutex_lock - acquire the mutex
 * @lock: the mutex to be acquired
 *
 * Lock the mutex exclusively for this task. If the mutex is not
 * available right now, it will sleep until it can get it.
 *
 * The mutex must later on be released by the same task that
 * acquired it. Recursive locking is not allowed. The task
 * may not exit without first unlocking the mutex. Also, kernel
 * memory where the mutex resides must not be freed with
 * the mutex still locked. The mutex must first be initialized
 * (or statically defined) before it can be locked. memset()-ing
 * the mutex to 0 is not allowed.
 *
 * ( The CONFIG_DEBUG_MUTEXES .config option turns on debugging
 *   checks that will enforce the restrictions and will also do
 *   deadlock debugging. )
 *
 * This function is similar to (but not equivalent to) down().
 */
void __sched mutex_lock(struct mutex *lock)

ロックする為の関数です。他のプロセスが既にロックを保持している場合、呼び出し元のプロセスはスリープします。引数には、mutex_initでも指定したstruct mutex型のポインタを指定します。

/**
 * mutex_unlock - release the mutex
 * @lock: the mutex to be released
 *
 * Unlock a mutex that has been locked by this task previously.
 *
 * This function must not be used in interrupt context. Unlocking
 * of a not locked mutex is not allowed.
 *
 * This function is similar to (but not equivalent to) up().
 */
void __sched mutex_unlock(struct mutex *lock)

ロックを解除する為の関数です。引数には、mutex_initでも指定したstruct mutex型のポインタを指定します。

spin_lock の使い方

 spin_lockは、ロックを取得できるまでプロセスをスピンさせ続ける排他制御です。spin_lockは、ロックを取得するまで動き続ける為、短時間の排他制御に適しています。spin_lockは、CPU負荷が大きい為、長時間の排他制御には適していません。主要な関数には、以下のようなものがあります。

kernel/mediatek/4.4/include/linux/spinlock.h

#define spin_lock_init(_lock)				\
do {							\
	spinlock_check(_lock);				\
	raw_spin_lock_init(&(_lock)->rlock);		\
} while (0)

spin_lockを初期化する為の関数です。引数にはspinlock_t型のポインタを指定します。

#define spin_lock_irqsave(lock, flags)				\
do {								\
	raw_spin_lock_irqsave(spinlock_check(lock), flags);	\
} while (0)

ロックする為の関数です。割り込みを禁止してからロックを取得します。取得した割り込みフラグの状態を保存し、ロックを解放する際に復元します。引数には、spin_lock_initでも指定したspinlock_t型のポインタを指定します。

static __always_inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
	raw_spin_unlock_irqrestore(&lock->rlock, flags);
}

ロックを解除する為の関数です。引数には、spin_lock_initでも指定したspinlock_t型のポインタを指定します。

最後に

 処理時間等を考慮して、どちらの排他制御を使用するか考える必要があります。割り込み処理等で、スリープに入るmutex_lockを実行して、長期間待たされると、割り込みを受け付けない期間により、システムが不安定になる場合があるので注意が必要です。spin_lock_irqsaveは、割り込みを禁止する為、ロックしている区間で使えない関数があるので注意が必要です。

コメント

タイトルとURLをコピーしました