spin_lock、spin_lock_bh、spin_lock_irq、spin_lock_IRqsaveの使用


概要


Spinlockの目的は,SMPにおいて複数のCPUが同時にアクセスする変数を同期させることである.Linuxでは、普通のspinlockは余分な意味を持たないので、逆に気をつけなければなりません.Linux kernelで実行されるコードは、normalとinterrupt contextの2つに大きく分けられます.tasklet/softirqはnormalに帰すことができます.彼らは待機に入ることができるからです.nested interruptはinterrupt contextの特殊な状況であり、もちろんinterrupt contextでもある.Normalレベルはinterruptによって切断され、interruptは別のinterruptによって切断されますが、normalによって中断されません.各interrupt間には優先度関係はなく,可能であれば各interruptは他のinterruptによって中断される.まず,単一CPUの場合を考える.このような場合,どの実行レベルにおいても,CPUの割り込みを簡単にオフにすれば独占処理の目的を達成できる.この観点からspinlockの実現は簡単に舌を巻く:cli/sti.このようにすれば、preemptionがもたらす複雑なドアを閉じます.単一CPUの場合は簡単ですが、マルチCPUはそんなに簡単ではありません.現在のCPUの割り込みを単純にオフにするだけでは、私たちに幸運をもたらすことはありません.私たちのコードが共有データにアクセスすると、別のCPUはいつでもこのデータを変更します.spinlockがこのために設定されていることを知らせる手段が必要です.

使用例

extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
spin_unlock(&lock);

彼は正常に仕事ができますか.答えは可能だ.場合によっては、このコードは正常に動作しますが、このようなことが起こるかどうかを考えてみましょう.
// in normal run level
extern spinlock_t lock;
// ...
spin_lock(&lock);
// do something
                                                     // interrupted by IRQ ...
                                                     // in IRQ
                                                     extern spinlock_t lock;
                                                     spin_lock(&lock);

私たちはnormalレベルでspinlockを獲得しました.私たちが何をしたいのか、私たちはinterruptに中断されました.CPUはinterrupt levelのコードを実行しました.それもこのspinlockを獲得したいと思っていました.そこで「デッドロック」が発生しました.解決策は次のとおりです.
extern spinlock_t lock;
// ...
cli; // disable interrupt on current CPU
spin_lock(&lock);
// do something
spin_unlock(&lock);
sti; // enable interrupt on current CPU

spinlockを取得する前に、現在のCPUの割り込みを禁止してから、lockを取得します.ロックを解放してから割り込みを開きます.これにより、デッドロックが防止されます.実際、Linuxはこの機能を実現するためにより迅速な方法を提供しています.
extern spinlock_t lock;
// ...
spin_lock_irq(&lock);
// do something
spin_unlock_irq(&lock);

nested interruptがなければ、これはすべて良いです.nested interruptを加えて、この例を見てみましょう.
// code 1
extern spinlock_t lock1;
// ...
spin_lock_irq(&lock1);
// do something
spin_unlock_irq(&lock1);
 
// code 2
extern spinlock_t lock2;
// ...
spin_lock_irq(&lock2);
// do something
spin_unlock_irq(&lock2);

Code 1とcode 2はinterruptで動作し、このような動作順序を簡単に考えることができます.
Code 1                                               Code 2

extern spinlock_t lock1;
// ...
spin_lock_irq(&lock1); 
                                                     extern spinlock_t lock2;
                                                     // ...
                                                     spin_lock_irq(&lock2);
                                                     // do something 
                                                     spin_unlock_irq(&lock2);
// do something
spin_unlock_irq(&lock1); 

問題はspin_を実行していることです.unlock_IRq(&lock 2)後にこのCPUの割り込みが開かれ、「デッドロック」の問題が戻ってくる!解決策は、割り込みを閉じるたびに現在の割り込みの状態を記録し、直接割り込みを開くのではなく回復することです.
unsigned long flags;
local_irq_save(flags);
cli; // disable interrupt on current CPU
spin_lock(&lock);
// do something
spin_unlock(&lock);
local_irq_restore(flags);

Linuxは、より簡単な方法を提供しています.
unsigned long flags;
spin_lock_irqsave(&lock, flags);
// do something
spin_unlock_irqrestore(&lock, flags);

まとめ


1)保護された共有リソースは、プロセスコンテキストアクセスおよびソフトブレークコンテキストアクセスのみで保護された共有リソースがプロセスコンテキストアクセスおよびソフトブレークコンテキストアクセスのみである場合、プロセスコンテキストで共有リソースにアクセスする場合、ソフトブレークによって中断される可能性があり、ソフトブレークコンテキストに入って保護された共有リソースにアクセスする可能性があるため、この場合、共有リソースへのアクセスにはspin_を使用する必要があるlock_bhとspin_unlock_bhが保護する.(歴史的な理由から、接尾辞「bh」は様々な下半部の通称となっている.実はspin_lock_bhはspin_lock_softirqと呼ぶべきだった).もちろんspin_を使いますlock_IRqとspin_unlock_IRqおよびspin_lock_IRqsaveとspin_unlock_IRqrestoreは、ローカルハード割り込みを失効させ、失効ハード割り込みは暗黙的にソフト割り込みを失効させることもできる.でもspin_を使うlock_bhとspin_unlock_bhは他の2つより速いのが最も適切です.
2)保護された共有リソースは、プロセスコンテキストおよびtaskletまたはtimerコンテキストでのみアクセスされます.保護された共有リソースがプロセスコンテキストおよびtaskletまたはtimerコンテキストでのみアクセスされる場合は、spin_も使用されます.lock_bhとspin_unlock_bhはtaskletとtimerがソフトブレークで実現されるため、ロックされたマクロを取得し、解放する.
3)保護された共有リソースは、1つのtaskletまたはtimerコンテキストでのみアクセスする.保護された共有リソースが1つのtaskletまたはtimerコンテキストでのみアクセスする場合、同じtaskletまたはtimerは、SMP環境でも1つのCPU上でのみ実行できるため、スピンロック保護は不要である.実際taskletはtaskletを呼び出していますscheduleタグは、スケジューリングが必要なときにtaskletを現在のCPUにバインドしているため、同じtaskletが他のCPUで同時に実行されることは決してありません.timerもadd_を使用していますtimerがtimerキューに追加されたときに現在のCPUに割り当てられているため、同じtimerが他のCPUで実行されることはありません.もちろん、同じtaskletで2つのインスタンスが同じCPUで同時に実行されるのはさらに不可能です.
4)保護された共有リソースが2つ以上のtaskletまたはtimerコンテキストでのみアクセス保護された共有リソースが2つ以上のtaskletまたはtimerコンテキストでのみアクセスされる場合、共有リソースへのアクセスはspin_lockとspin_unlockは保護するため、使用する必要はありません.bhバージョンは、taskletまたはtimerが実行されている場合、現在のCPU上で他のtaskletまたはtimerが実行される可能性がないためです.
5)保護された共有リソースが1つのソフトブレーク(taskletとtimerを除く)コンテキストのみでアクセス保護された共有リソースが1つのソフトブレーク(taskletとtimerを除く)コンテキストのみでアクセスする場合、この共有リソースはspin_lockとspin_同じソフトブレークが異なるCPU上で同時に実行できるため、unlockは保護されます.
6)保護された共有リソースが2つ以上のソフトブレークコンテキストでアクセス保護された共有リソースが2つ以上のソフトブレークコンテキストでアクセスされている場合、この共有リソースは当然spin_lockとspin_unlockは保護し,異なるソフトブレークは異なるCPU上で同時に動作することができる.
7)保護された共有リソースは、ソフトブレーク(taskletおよびtimerを含む)またはプロセスコンテキストおよびハードブレークコンテキストでアクセス保護された共有リソースがソフトブレーク(taskletおよびtimerを含む)またはプロセスコンテキストおよびハードブレークコンテキストでアクセスする場合、ソフトブレークまたはリードコンテキストアクセス中にハードブレークによってブレークされ、ハードブレークコンテキストに入って共有リソースにアクセスすることができるため、プロセスまたはソフトブレークコンテキストではspin_を使用する必要があります.lock_IRqとspin_unlock_IRqは共有リソースへのアクセスを保護します.割り込み処理ハンドルで使用されるバージョンは、状況に応じて異なります.共有リソースにアクセスする割り込み処理ハンドルが1つしかない場合、割り込み処理ハンドルにはspin_のみが必要です.lockとspin_unlockは共有リソースへのアクセスを保護すればよい.割り込み処理ハンドルを実行している間、同じCPU上のソフト割り込みやプロセスによって割り込まれることはあり得ないからです.ただし、共有リソースにアクセスする割り込み処理ハンドルが異なる場合は、割り込み処理ハンドルでspin_を使用する必要があります.lock_IRqとspin_unlock_IRqは共有リソースへのアクセスを保護します.spin_を使用していますlock_IRqとspin_unlock_IRqの場合、完全にspin_lock_IRqsaveとspin_unlock_IRqrestoreの代わりに、具体的にはどちらを使うべきかは状況によって異なります.共有リソースへのアクセス前に割り込みが可能であると確信できる場合はspin_を使用します.lock_IRqはspinより優れているのでlock_IRqsaveは少し速くしなければなりません.しかし、イネーブルを中断するかどうかを判断できない場合はspin_を使用します.lock_IRqsaveとspin_unlock_IRqrestoreは、共有リソースへのアクセスを直接中断させるのではなく、共有リソースへのアクセス前の中断フラグを回復するため、より良い.もちろん、共有リソースへのアクセス時に失効を中断する必要があり、アクセスが完了した後にイネーブルを中断する必要がある場合もあります.この場合、spin_lock_IRqとspin_unlock_IRqが一番いいです.
spin_ロックは、異なるCPU上の実行ユニットが共有リソースに同時にアクセスし、異なるプロセスコンテキストが互いにプリエンプトすることによる共有リソースへの非同期アクセスを阻止するために使用されるが、割り込み失効とソフト割り込み失効は、同じCPU上で共有リソースへの非同期アクセスをソフト割り込みまたは中断することを阻止するために使用される.
原文住所:https://blog.csdn.net/wesleyluo/article/details/8807919