Linux カーネル コードから、preempt_enable()
とpreempt_disable()
はただbarrier()
:
#define preempt_disable() barrier()
#define preempt_enable() barrier()
理解できません。barrier()
プリエンプションを無効または有効にするのにa だけで十分なのはなぜですか?
Linux カーネル コードから、preempt_enable()
とpreempt_disable()
はただbarrier()
:
#define preempt_disable() barrier()
#define preempt_enable() barrier()
理解できません。barrier()
プリエンプションを無効または有効にするのにa だけで十分なのはなぜですか?
カーネル v4.3 では、 の正しい定義preempt_enable
は次のとおりです。
#define preempt_enable() \
do { \
barrier(); \
if (unlikely(preempt_count_dec_and_test())) \
__preempt_schedule(); \
} while (0)
同様に forpreempt_disable
はここにあります:
#define preempt_disable() \
do { \
preempt_count_inc(); \
barrier(); \
} while (0)
preempt_enable
プリエンプションが有効になる前に最適化バリアをpreempt_disable
挿入し、プリエンプション カウンターがインクリメントされた後にバリアを挿入します。ただし、コメントによると、プリエンプションは含まれず、プリエンプトされた領域を保護するための障壁のみが含まれる場合。
編集: UP と非プリエンプトでは、スピンロックとプリエンプションの無効化/有効化ポイントが完全にスタブ化されます。これは、保護することを意図した種類の同時実行性を達成できる通常のコードがないためです。
ただし、スケジューリングを引き起こす可能性のある通常のコードはありませんが、 そうすることができるいくつかの例外的な (文字通り!) コードが発生することになり、コンパイラによってクリティカル領域に移動されないようにする必要があります。
特に、get_user() と put_user() は通常、インライン asm ステートメントとして実装され (インライン asm が行外で呼び出す呼び出し命令を行う場合でも)、結果として明らかにページ フォールトと IO を引き起こす可能性があります。 . そのインライン asm がプリエンプション セーフ (またはスピンロックで保護された) コード領域の途中にスケジュールされている場合、明らかに負けます。
確かに、これが実際に発生する可能性は非常に低く、これに関連する実際のバグの例は確認されていません。しかし、トリガーするのが非常に難しく、結果として生じるバグが非常に微妙であるため、これを正しく行うには細心の注意を払う必要があります。
したがって、プリエンプションが無効になっている場合でも、プリエンプションが無効な領域にいることをシステムに明示的に伝えるために実際のコードを生成する必要がない場合でも、少なくともコンパイラーに物事を移動しないように伝える必要があることを確認してください。クリティカル領域。ソース