並行処理と競爭狀態 (※マルチコア対応関連)
1)発生原因
SMPシステムでは、異なるプロセッサ上でコードを同時に実行することができます。
カーネルコードはプリエンプティブであり、ドライバコードはいつでもプロセッサを手放します。
・複數のプロセスが同一ドライバを呼ばれた場合
・割り込みハンドラ
・非同期のカーネルイベントへの応答
など
(性質上、ハードウェアリソースは共有されるものであり、しばしばソフトウェアリソースも複數のスレッドから利用する必要があります。)
2)対策
アクセス管理の一般的なテクニックは、「ロッキング」または「相互排他」と呼ばれます。
ロック效能測定は、【lockmeter】というツールを使えば、カーネルはロックでの待ち時間を測定できる。
①セマフォとミューテックス
・関連構造體: struct semaphore
・関連関數
void sema_init(struct semaphore *sem, int val);
DECLARE_MUTEX(name);
DECLARE_MUTEX_LOCKED(name);
void init_MUTEX(struct semaphore *sem);
void init_MUTEX_LOCKED(struct semaphore *sem);
void down(struct semaphore *sem);
int down_interruptible(struct semaphore *sem);
int down_trylock(struct semaphore *sem);
void up(struct semaphore *sem);
②Reader/Writerのセマフォ
・関連構造體: struct rw_semaphore
・関連関數
void init_rwsem(struct rw_semaphore *sem);
void down_read(struct rw_semaphore *sem);
int down_read_trylock(struct rw_semaphore *sem);
void up_read(struct rw_semaphore *sem);
void down_write(struct rw_semaphore *sem);
int down_write_trylock(struct rw_semaphore *sem);
void up_write(struct rw_semaphore *sem);
void downgrade_write(struct rw_semaphore *sem);
③Completion
Completionは、あるタスクが別のタスクに、ジョブが完了したことを通知する。
・関連構造體: struct completion
・関連関數
DECLARE_COMPLETION(my_completion);
init_completion(&my_completion);
INIT_COMPLETION(struct completion c);
void wait_for_completion(struct completion *c);
void complete(struct completion *c);
void complete_all(struct completion *c); // completionはひとつのスレッドへ、completion_allはすべてのスレッドへ。
void complete_and_exit(struct completion *c, long retval); // Completionを待つ。
④スピンロック
セマフォと異なり、スピンロックは、割り込みハンドラのようなスリップできないコードで使われる。
效能がセマフォより高い。
ロック済、未ロックといった2種類狀態しかない。
・関連構造體:
・関連関數
spinlock_t my_lock = SPIN_LOCK_UNLOCKED;
void spin_lock_init(spinlock_t *lock);
void spin_lock(spinlock_t *lock);
void spin_unlock(spinlock_t *lock);
void read_lock(rwlock_t *lock);
void read_unlock(rwlock_t *lock);
void write_lock(rwlock_t *lock);
void write_unlock(rwlock_t *lock);
など。
・スピンロックの原則
スピンロックを保持している間は、
一、どのコードもアトミックでないといけない。
二、割り込みを無効にする必要がある。
三、できるだけ最小限の時間に限る。
★ ⑤ロックに代わる方法
マルチコアの場合、ロックが適當な対策でない場合がある。
代わりにアトミックなアクセスや、アルゴリズムで対策する。
・環狀バッファ (特にネットワークアダプタでは、環狀バッファを使うのが一般的です。<inux/kfifo.h>)
・整數操作アトミック (atomic_t。一部プロセッサ制限のため、24ビットを超える分は使えない。 <asm/atomic.h>)
void atomic_set(atomic_t *v, int i);
int atomic_read(atomic_t *v);
void atomic_add(int i, atomic_t *v);
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);
など。
・ビット操作アトミック (アーキテクチャ依存。<asm/bitops.h>
void set_bit(nr, void *addr);
void clear_bit(nr, void *addr);
void change_bit(nr, void *addr);
など。
・seqlock (<linux/seqlock.h>)
Linux2.6からは、速く、ロックなしで共有リソースにアクセスできる新しい仕組みseqlockが組み込まれました。
seqlockは保護するリソースが小さく、単純で、頻繁にアクセスされて、書き込みは稀だが速さが要求される場合に使用します。
seqlockは、ポインタを含むデータ構造體を保護するためには使えません。
データ型: seqlock_t
関連関數:
unsigned int read_seqbegin_irqsave(seqlock_t *lock, unsigned long flags);
int read_seqretry_irqrestore(seqlock_t *lock, unsigned int seq, unsigned long flags);
void write_seqlock(seqlock_t *lock);
void write_sequnlock(seqlock_t *lock);
など。