Linux Kernel Documentationからこれを検討することもできます。
C プログラマーは、変数が現在の実行スレッドの外部で変更される可能性があることを意味するために volatile を使用することがよくあります。その結果、共有データ構造が使用されているときに、カーネル コードで使用したくなることがあります。言い換えれば、それらは volatile 型を一種の簡単なアトミック変数として扱うことが知られていますが、そうではありません。カーネル コードでの volatile の使用は、ほとんど正しくありません。このドキュメントでは、その理由について説明します。
volatile に関して理解しておくべき重要な点は、その目的が最適化を抑制することであり、これが実際にやりたいことであるということはほとんどないということです。カーネルでは、共有データ構造を不要な同時アクセスから保護する必要がありますが、これはまったく別のタスクです。不要な同時実行から保護するプロセスは、最適化に関連するほぼすべての問題をより効率的な方法で回避します。
volatile と同様に、データへの同時アクセスを安全にするカーネル プリミティブ (スピンロック、ミューテックス、メモリ バリアなど) は、不要な最適化を防ぐように設計されています。それらが適切に使用されている場合、volatile も使用する必要はありません。volatile がまだ必要な場合は、ほぼ確実にコードのどこかにバグがあります。適切に記述されたカーネル コードでは、volatile は動作を遅くするだけです。
カーネル コードの典型的なブロックを考えてみましょう。
spin_lock(&the_lock);
do_something_on(&shared_data);
do_something_else_with(&shared_data);
spin_unlock(&the_lock);
すべてのコードがロック規則に従っている場合、the_lock が保持されている間、shared_data の値が予期せず変更されることはありません。そのデータを操作する可能性のある他のコードは、ロックで待機します。スピンロック プリミティブは、メモリ バリアとして機能します (そのように明示的に記述されています)。つまり、データ アクセスはそれらをまたがって最適化されません。そのため、コンパイラは shared_data に何が含まれるかを知っていると考えるかもしれませんが、spin_lock() 呼び出しはメモリ バリアとして機能するため、知っていることはすべて強制的に忘れさせます。そのデータへのアクセスに最適化の問題はありません。
shared_data が volatile と宣言された場合でも、ロックは必要です。しかし、他の誰もそれを操作できないことがわかっている場合、コンパイラはクリティカル セクション内のshared_data へのアクセスを最適化することもできなくなります。ロックが保持されている間、shared_data は揮発性ではありません。共有データを扱う場合、適切なロックにより volatile が不要になり、有害になる可能性があります。
volatile ストレージ クラスは、もともとメモリ マップド I/O レジスタ用でした。カーネル内では、レジスタ アクセスもロックで保護する必要がありますが、コンパイラがクリティカル セクション内のレジスタ アクセスを「最適化」することも望ましくありません。ただし、カーネル内では、I/O メモリ アクセスは常にアクセサ関数を介して行われます。ポインタを介して I/O メモリに直接アクセスすることは嫌われており、すべてのアーキテクチャで機能するとは限りません。これらのアクセサーは不要な最適化を防ぐために作成されているため、ここでも volatile は不要です。
volatile を使用したくなるもう 1 つの状況は、プロセッサが変数の値を待機中の場合です。ビジー待機を実行する正しい方法は次のとおりです。
while (my_variable != what_i_want)
cpu_relax();
cpu_relax() 呼び出しは、CPU の消費電力を下げるか、ハイパースレッド化されたツイン プロセッサに譲ることができます。それはたまたまメモリバリアとしても機能するため、繰り返しになりますが、volatile は不要です。もちろん、ビジー ウェイティングは一般的に、そもそも反社会的行為です。
カーネルで volatile が意味を持つまれな状況がまだいくつかあります。
上記のアクセサ関数は、ダイレクト I/O メモリ アクセスが機能するアーキテクチャで volatile を使用する場合があります。基本的に、各アクセサー呼び出しはそれ自体で少し重要なセクションになり、プログラマーの期待どおりにアクセスが行われるようにします。
メモリを変更するが、他に目に見える副作用がないインライン アセンブリ コードは、GCC によって削除されるリスクがあります。volatile キーワードを asm ステートメントに追加すると、この削除を防ぐことができます。
jiffies 変数は、参照されるたびに異なる値を持つことができるという点で特別ですが、特別なロックなしで読み取ることができます。したがって、jiffies は揮発性になる可能性がありますが、このタイプの他の変数の追加は強く嫌われます。この点で、Jiffies は「愚かな遺産」の問題 (Linus の言葉) と見なされます。それを修正することは、価値があるよりも面倒です。
I/O デバイスによって変更される可能性のあるコヒーレント メモリ内のデータ構造へのポインターは、合法的に揮発性になる場合があります。このタイプの状況の例としては、ネットワーク アダプターによって使用されるリング バッファーが挙げられます。この場合、そのアダプターはポインターを変更して、どの記述子が処理されたかを示します。
ほとんどのコードでは、上記の volatile の正当化はどれも当てはまりません。その結果、volatile の使用はバグと見なされる可能性が高く、コードをさらに精査する必要があります。volatile を使用したくなる開発者は、一歩下がって、自分が本当に達成しようとしていることについて考えるべきです。