Linux カーネルは、プロセス スケジューリング、メモリ管理、デバイス ドライバー、ネットワーク プロトコルなど、さまざまな同時実行性の問題に対処する必要がある複雑なシステムです。データの一貫性と正確性を保証するために、Linux カーネルはスピン ロック、セマフォ、読み取り/書き込みロックなどのさまざまな同期メカニズムを提供します。ただし、これらの同期メカニズムには次のようないくつかの欠点があります。
RCU は、ロックの競合とコンテキストの切り替えを削減し、同時実行パフォーマンスとシステムの応答性を向上させることができます;
リーリー
この実装には 3 つの関数呼び出しがあるように見えますが、実際の作業は最初の関数 __rcu_read_lock() によって行われます。__rcu_read_lock() は preempt_disable() を呼び出してカーネルのプリエンプティビリティをオフにします。ただし、割り込みは許可されます。リーダーが rcu クリティカル セクションにいて、共有データ領域のポインター p を読み取ったばかりであるとします (ただし、p 内のデータ メンバーにはまだアクセスしていません)。割り込みが発生し、割り込み処理の例は次のとおりです。 ISR は、p が指すデータ領域を変更する必要がある場合があります。RCU の設計原則に従って、ISR は同じサイズのデータ領域 new_p を新たに割り当て、古いデータ領域 p のデータを新しいデータ領域にコピーします。データ領域、次に new_p 基本的に、ISR の後にデータ変更作業を実行します (new_p 空間で変更されるため、p への同時アクセスはなく、RCU はロックフリーのメカニズムであり、これが理由です)。データ更新作業を完了し、new_p を p に代入し (p=new_p)、最後にコールバック関数を登録して、適切なタイミングで古いポインタ p を解放します。したがって、古いポインタ p への参照がすべて終了している限り、p を解放しても問題はありません。割り込み処理ルーチンがこれらのタスクの完了から戻ったとき、割り込まれたプロセスは引き続き p 空間のデータ、つまり古いデータにアクセスしますが、この結果は RCU メカニズムによって許可されています。#RCU ルールでは、リーダーとライター間のポインターの切り替えによって発生する一時的なリソース ビューの不一致が許容されます。
RCU に関する次の興味深い質問は、古いポインタはいつ解放されるのかということです。これに対する答えは、多くの本で見てきましたが、「システム内のすべてのプロセッサでプロセスの切り替えが発生したとき」です。この様式化された答えは、RCU メカニズムを初めて使用する読者を混乱させることがよくありますが、コールバック関数を呼び出して古いポインタを解放する前に、すべてのプロセッサでプロセスの切り替えが発生するのをなぜ待たなければならないのでしょうか?これは実際には RCU の設計ルールによって決定されます。 古いポインターへのすべての参照は、rcu_read_lock および rcu_read_unlock に含まれるクリティカル セクションでのみ発生でき、このクリティカル セクションではプロセスの切り替えはできません, Onceクリティカル セクションがクリティカル セクションの外にある場合、古いポインター p へのいかなる形式の参照も存在しないはずです。プロセスが切り替わると、古いポインタを解放するコールバック関数が呼び出され、古いポインタが解放される可能性があるため、このルールでは、リーダーがクリティカル セクションでプロセスを切り替えることができないことが明らかに要求されます。再スケジュールされると、解放されたメモリ領域を参照する可能性があります。
rcu_read_lock がカーネル プリエンプティビリティをオフにするだけでよい理由がわかりました。クリティカル セクションで割り込みが発生した場合でも、現在のプロセスを切り替えたり削除したりすることが不可能になるからです。 カーネル開発者、というか RCU 設計者ができることは限られています 。次のステップはユーザーの責任ですが、RCU のクリティカル セクションで関数が呼び出された場合、関数がスリープする可能性があり、RCU の設計ルールに違反し、システムが不安定な状態になります。
このことからも、何かを利用するにはその内部の仕組みを理解する必要があることが改めて分かります 先ほどの例のように、今はプログラムに問題がなくても、システムには潜在的な危険性が残されているのです。時限爆弾: いつでも爆発する可能性があり、特に長い時間が経った後に突然問題が発生した場合にはそうです。ほとんどの場合、問題を見つけるのにかかる時間は、落ち着いて RCU の原則を注意深く理解するのにかかる時間よりもはるかに長い場合があります。
RCU のリーダーは、rwlock のリーダーよりも自由度が高くなります。 RCU リーダーは、共有リソースにアクセスするときにライターの感情を考慮する必要がないため、rwlock ライターとは異なり、rwlock リーダーは、共有リソースを読み取るときに、ライターがリソースを操作していないことを確認する必要があります。 この 2 つの違いは、RCU ではリーダーとライターの間で共有リソースが分離されているのに対し、rwlock のリーダーとライターは最初から最後まで共有リソースのみを使用することに由来します。これは、RCU のライターがより多くの責任を負わなければならず、同じ共有リソースを更新する複数のライター間で何らかの相互排他メカニズムを導入する必要があることも意味します。そのため、RCU は「ロックフリー メカニズム」です。このステートメントはリーダーと読者に限定されます。作家たち。したがって、読み取り操作が多数あり、更新操作が比較的少ない状況では、RCU メカニズムを使用する必要があることがわかります。現時点では、RCU の読み取り操作には他のロック メカニズムと比べてロック オーバーヘッドがほとんどないため、RCU はシステム パフォーマンスを大幅に向上させることができます。
実際の使用では、共有リソースはリンク リストの形式で存在することがよくあります。カーネルは、RCU モードでのリンク リスト操作用のいくつかのインターフェイス関数を実装しています。リーダーとユーザーは、list_add_tail_rcu、list_add_rcu、hlist_replace_rcu などのこれらのカーネル関数を使用する必要があります。具体的な使用方法については、カーネル プログラミングまたはデバイス ドライバーの情報を参照してください。古いポインターの解放に関して、Linux カーネルはユーザーが使用できる 2 つの方法を提供します。1 つは call_rcu を呼び出す方法、もう 1 つは synchronize_rcu を呼び出す方法です。前者は非同期メソッドです。call_rcu は、古いポインタをノードに解放するコールバック関数を配置し、現在 call_rcu を実行しているプロセッサのローカル リンク リストにノードを追加します。クロック割り込みの Softirq 部分 (RCU_SOFTIRQ ) 、rcu ソフト割り込み処理関数 rcu_process_callbacks は、現在のプロセッサがスリープ期間 (カーネル プロセスのスケジューリングやその他の側面を含む静止状態) を経験したかどうかを確認します。rcu のカーネル コード実装は、システム内のすべてのプロセッサがスリープ期間を経験したかどうかを判断します。期間 (すべてのプロセッサでプロセスの切り替えが発生したため、この時点で古いポインタを安全に解放できることを意味します) が経過すると、call_rcu によって提供されるコールバック関数が呼び出されます。
synchronize_rcu の実装は待機キューを利用します。実装中に、call_rcu のように現在のプロセッサのローカル リンク リストにノードも追加されます。call_rcu との違いは、このノードのコールバック関数が wakeme_after_rcu であり、次に synchronize_rcu であることです。システム内のすべてのプロセッサでプロセスの切り替えが発生するまで、wakeme_after_rcu は待機キューでスリープし、スリープしている synchronize_rcu をウェイクアップするために rcu_process_callbacks によって呼び出されます。ウェイクアップされた後、synchronize_rcu は古いポインタを解放できることを認識します。
この記事では、Linux カーネルの効率的な同期メカニズムである RCU について紹介します。これは、パブリッシュ/サブスクライブ モデルに基づく同期メカニズムです。 RCU の基本的な考え方、主要なインターフェイス、実装の詳細を原理的な側面から分析し、対応するコード例を示しました。また、アプリケーションの観点から、リンク リスト操作、タイマー管理、ソフト割り込み処理、その他のシナリオでの RCU の使用法を紹介し、対応するコード例を示しました。この記事を学習することで、RCU の基本的な使用法をマスターし、実際の開発で RCU を柔軟に使用して効率的な同期要件を実現できるようになります。この記事がお役に立てば幸いです!
以上がLinux カーネルの RCU メカニズムの詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。