
2026/01/17 0:04
Read_once() と Write_once() は存在しますが、Rust には該当機能はありません。
RSS: https://news.ycombinator.com/rss
要約▶
日本語訳:
改訂概要
Linux カーネルの
READ_ONCE() と WRITE_ONCE() マクロは、コンパイラレベルで原子性を強制しつつ順序付け制約を課さないように 3.18 バージョンから追加されました。これらのマクロにはほぼ8 000箇所の呼び出しがあります。2025年12月、Alice Ryhl はこれらのマクロの Rust 対応版を導入するパッチシリーズを投稿しましたが、Rust 版は単に C の read_volatile() / write_volatile() をラップしたものです。しかし、多くのカーネル Rust 開発者は、これらを公開することに反対しました。理由は、そのセマンティクスが原子性とレース防止の区別を曖昧にし、Rust のより明確な緩和モード Atomics(Atomic::from_ptr().load(Relaxed))とは異なるからです。Gary Guo、Boqun Feng、Peter Zijlstra らによる議論でこの曖昧さが浮き彫りになりました。その結果、コミュニティは現時点ではカーネルにマクロを追加するのを中止し、Rust では明示的な Atomic API を採用する方向へと決定しました。この決定により、C コードは適切な場面(例えば高解像度タイマーで欠落した呼び出しが見つかった後)で WRITE_ONCE() の使用を継続できますが、同時に Rust と C は単一の統一設計へ統合するよりも、それぞれ別個の並行性プリミティブを維持する可能性が高いことを示唆しています。本文
[LWN サブスクライバー限定コンテンツ]
LWN.netへようこそ
以下のサブスクリプション専用コンテンツは、LWN のサブスクライバーによってご利用いただけるものです。数千人のサブスクライバーが LWN を通じて Linux およびフリーソフトウェアコミュニティから最高のニュースを受け取っています。この記事がお役に立った場合は、ぜひ LWN の購読をご検討ください。LWN.net へのご訪問ありがとうございます!
READ_ONCE() と WRITE_ONCE() マクロはカーネル内で頻繁に使用されており、READ_ONCE() の呼び出し先は約 8,000 件あります。これらは多くのロックレスアルゴリズムの実装に不可欠であり、一部のデバイスメモリアクセスでは必要になる場合があります。そのため、カーネル内の Rust コードが増えるにつれて、同様のマクロの Rust バージョンも登場するはずだと考えられます。しかし実際には、Rust コミュニティは並行データアクセスに対して別のアプローチを取ろうとしているようです。
READ_ONCE() と WRITE_ONCE() の理解は、カーネル開発者が任意の並行データアクセスに対応する上で重要です。自然なこととして、これらはカーネルドキュメントからほぼ完全に除外されています。以下の説明は include/asm-generic/rwonce.h の冒頭にあります。
Prevent the compiler from merging or refetching reads or writes. The compiler is also forbidden from reordering successive instances of READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some particular ordering. One way to make the compiler aware of ordering is to put the two invocations of READ_ONCE or WRITE_ONCE in different C statements.
つまり、
READ_ONCE() の呼び出しはコンパイラに対して指定された場所から正確に一度だけ読み取るよう強制し、最適化によって読み取りが除外または繰り返されることを防ぎます。WRITE_ONCE() も同様に書き込みについて同じ効果があります。また、これらは原子性を保証します:あるタスクが READ_ONCE() を使って場所を読み取っている間に別のタスクがその場所を書き込む場合、読み取りは書き込み前か後ろでの値を返し、両者のランダムな組み合わせにはならないということです。これらのマクロは、smp_load_acquire() のように強い順序付け要件を持つマクロとは異なり、上記以外にコンパイラや CPU に対して順序制約を課しません。
READ_ONCE() と WRITE_ONCE() は 2014 年の 3.18 リリースで追加されました。最初は WRITE_ONCE() が ASSIGN_ONCE() と呼ばれていましたが、3.19 の開発サイクル中に名前が変更されました。
2025 年末、Alice Ryhl は Rust 用に
READ_ONCE() と WRITE_ONCE() を実装するパッチシリーズを投稿しました。彼女は、volatile 読み取りをこれらの呼び出しで置き換えられる箇所がコード中にあると述べており、そのシリーズでは構造体 file の f_flags フィールドへのアクセスを READ_ONCE() を使うよう変更しました。実装は多くの Rust マクロ魔法を含みますが、最終的には Rust の read_volatile() と write_volatile() 関数を呼び出します。
しかし他のカーネル Rust 開発者たちは反対しました。Gary Guo は
READ_ONCE() と WRITE_ONCE() を公開するよりも、Rust の Atomic クレートからリラックスした操作(Atomic::from_ptr().load(Relaxed) など)を使う方が良いと主張しました。Boqun Feng はその反対意見をさらに掘り下げました。
とREAD_ONCE()の問題は、その意味合いが複雑である点にあります。時には原子性のため、また別の場面ではデータレース防止のために使われます。したがって私たちは Rust でも LKMM(Linux カーネルメモリモデル)を使用しますが、可能な限り API の意図を明確にする必要があります。WRITE_ONCE()を使うことでその点が改善されます。Atomic::from_ptr().load(Relaxed)私の見解では、
は数多くの問題に対する「応急処置」的解決策であり、それらを導入すると並行プログラミングへのより明確なビジョンの構築が妨げられます。READ_ONCE()/WRITE_ONCE()
要するに、Atomic クレートを使うことで開発者は操作が必要とする保証を正確に指定できるため、期待や要求が明確になります。この観点から Ryhl はカーネル Rust コードへの追加を一時的に止めました—少なくとも現時点では。
この結果が続く場合、興味深い影響があります。まず、Rust コードがコアカーネルに深く浸透するにつれて、その並行アクセスパターンは同等の C コードとは大きく異なる形になります。同じデータを扱っていてもです。ロックレスデータアクセスの理解はすでに単一 API で十分に難しく、今後は二つの API を理解しなければならないため、作業がさらに複雑化します。
同時に、この議論は C コードにも注目を集めました。Feng はまだ多くのカーネル C コードが「単純な書き込みが原子である」と仮定していることを指摘し、C 標準は明示的にそれを否定しています。Peter Zijlstra はそのようなコードはすべて
WRITE_ONCE() を正しく使用するよう更新すべきだと回答しました。このコードを探すのは難しいかもしれませんが(KCSAN が助けになる)、すべてを更新するには時間がかかります。会話では、C の高解像度タイマーコードに必要な READ_ONCE() 呼び出しが欠落している箇所も特定されました—Rust 作業が C に改善をもたらしたもう一つの例です。
過去の Rust 抽象化設計に関する議論では、C と大きく異なる Rust インターフェース作成への抵抗がありました(2024 年の記事を参照)。
READ_ONCE() と WRITE_ONCE() に対する Rust アプローチが元のものより優れていると判断されれば、同様のプロセスがここでも行われるべきだと言えます。数千に及ぶ低レベルの並行性プリミティブを、より正確な意味合いで指定し直すことは、勇気のない者には手が届かない作業になるでしょう。最終的には、両言語でコードが異なる方法で動作するケースに留まる可能性があります。
記事の索引エントリ Kernel Development / Rust Kernel Lockless algorithms