オペレーティング システムのパフォーマンス向上のためのカーネル ロックの最適化

パフォーマンスは最も重要であり、システム パフォーマンスの向上はすべてのエンジニアの追求です。現在、パフォーマンスの最適化は、システム ソフトウェア スタックの非効率性を取り除くこと、またはオーバーヘッドの高いシステム操作をバイパスすることに重点を置いています。たとえば、カーネル バイパスは、いくつかの操作をユーザー空間に移動し、特定のクラスのアプリケーションの基礎となるオペレーティング システムをリファクタリングすることによって、この目標を達成します。

多くの分野では、アプリケーションとカーネルの両方、さらには異なるカーネル サブシステム間でさえ、特殊化がパフォーマンス向上への答えであるようです。特に、特殊化により、アプリケーションがシステムに特定の機能を要求するコンテキストを構築できます。アプリケーションの特殊化とカーネルはストレージ、ネットワーキング、およびアクセラレータをバイパスしますが、カーネル内の同時実行制御は全体的なパフォーマンスにとって重要となる可能性があります。

af6e92d3997d53bd27210b0d7a3e1f7f.jpeg

1. オペレーティング システムのパフォーマンス: カーネル ロック

カーネル ロックは、共有リソースへのプロセス アクセスを制御するメカニズムです。Linux カーネルでは、プロセス作成時に特別なロックを割り当てることによってカーネル ロックが実装されます。プロセスが共有リソースにアクセスする必要がある場合、カーネルはプロセスがすでにロックを保持しているかどうかを確認し、そうでない場合は、そのプロセスをロックを待機するキューに追加し、他のプロセスがロックを解放するのを待ちます。

カーネル ロックはアプリケーションの優れたパフォーマンスとスケーラビリティを達成するために重要ですが、カーネル同期プリミティブは通常目に見えず、アプリケーション開発者の手の届かないところにあります。ロック アルゴリズムの設計とその正しさの検証はすでに困難ですが、ハードウェアの異種性が高まるとさらに困難になります。ロックが動作する環境、優先順位の逆転やロック所有者の優先順位などの問題に対する開発者の認識の欠如は、本質的にコンテキストの欠如です。

ユーザー空間アプリケーションがカーネル内の同時実行制御を調整する方法はありますか?

たとえば、ユーザーは一連のロックを保持する特定のタスクやシステム コールに優先順位を付けることができます。ユーザーは、非対称マルチプロセッシング対応ロックなどのハードウェア固有のポリシーを強制でき、特定のワークロードに基づいて読み取りと書き込みの優先順位を付けることができます。開発者がカーネル内のさまざまなロックを調整したり、それらのパラメータや動作を変更したり、異なるロック実装間で変更したりできる場合、システムのパフォーマンスをさらに向上させることができる可能性があります。

ソフトウェア スタックの特化は、アプリケーションのパフォーマンスを向上させる新しい方法であり、パフォーマンスを目的としてコードをコアにプッシュし、コア数の増加によるボトルネックを回避することでアプリケーションのスケーラビリティを向上させることを提案しています。時間が経つにつれ、Linux のようなモノリシック カーネルでも、ユーザー空間アプリケーションがカーネルの動作をカスタマイズできるようになりました。開発者は eBPF を使用して、トレース、セキュリティ、さらにはパフォーマンスの目的でカーネルをカスタマイズできます。

eBPF に加えて、Linux 開発者は、非同期 IO 操作を高速化するために、ユーザー空間とカーネル間の共有メモリ リング バッファである io_uring も使用しています。また、今日のアプリケーションは、オンデマンドのページングを完全にユーザー空間で処理できます。

アプリケーションは、基礎となるカーネルの同時実行メカニズムを制御し、ロック設計者やアプリケーション開発者にさまざまな機会を提供します。

30c0203fd2d8ff2a815ff370069c651b.jpeg

2. ロック: 過去、現在、未来

ハードウェアはロックのスケーラビリティを決定する主要な要素であるため、アプリケーションのスケーラビリティに影響を与えます。たとえば、キュ​​ーベースのロックを使用すると、複数のスレッドが同時にロックを取得するときに過剰なトラフィックを削減できます。同時に、階層ロックはバッチ処理を使用して、キャッシュ ラインのスラッシングの問題を最小限に抑えます。

SHFLLock は、カーネル メモリのオーバーヘッドとパフォーマンスの低下を軽減するために、ロック戦略と実装を分離することでロック アルゴリズムを設計するという新しいアイデアを提案します。主に、キューの順序を変更したり、待機中のスレッドの状態を変更したりするシャッフラー プログラムの概念を紹介します。ShflLocks はポリシーを強制する方法を提供しますが、ロックの取得/解放 API の単純なセットよりも一般的なポリシーに焦点を当てる試みもあります。アプリケーションのニーズに応えるために、アプリケーション開発者は、特定のワークロードに影響を与える特定のカーネル ロックを分析することで、制御された安全な方法でポリシーを定義し、シャフラーを使用してポリシーを適用してロック取得ポリシーを動的に更新する必要があります。

3 典型的なシナリオ: ロックを待機しているスレッドのスケジュール設定

ロックを待機しているスレッドは 2 つの異なる方法でスケジュールできます。1 つはロックが取得される順序に基づく取得認識スケジューリング、もう 1 つはクリティカル セクション内でスレッドが費やす時間に基づく占有認識スケジューリングです。

3.1 取得認識のスケジュール設定

ロック スイッチ。開発者はさまざまなロック アルゴリズムを切り替えることができます。顕著な状況が 3 つあります。

  1. 読み取り集中型のワークロードでは、ニュートラルなリーダー/ライター ロック設計から CPU ごとまたは NUMA ベースのリーダー設計に切り替えます。たとえば、ページ フォールトやディレクトリ内のファイルの列挙などです。もう 1 つのケースは、ニュートラルな読み取り/書き込みロック スイッチからのものです。純粋な書き込みロックの例としては、ディレクトリ内に複数のファイルを作成する場合があります。

  2. Numa ベースのロック設計から、ロック ホルダーが待機中のスレッドに代わって操作を実行する、Numa 認識と組み合わせたアプローチに切り替えます。このアプローチでは、キャッシュされた転送が少なくとも 1 つ削除されるため、パフォーマンスが向上します。

  3. たとえば、SHFLLock のシャッフラー機能の停止/ウェイク戦略をオフにして、ブロッキング読み取りを非ブロッキング読み取り/書き込みロック (rwlock) に切り替えることにより、ブロッキング ロックと非ブロッキング ロックを切り替えたり、その逆を切り替えます。

このアプローチには 2 つの利点があります。まず、開発者は、非ブロッキング ロックの使用や待機イベントを使用して、Btrfs ファイル システムで一般的に使用される停止/ウェイク戦略を実装するなど、一時的な同期を削除できます。2 番目に、開発者は複数のポリシーを動的に多重化することでロックの設計を統一できます。

e6c71a7665fc272339fd56b12058c7d4.png

3.1.1 ロックの継承

プロセスは、操作を実行するために複数のロックを取得する場合があります。たとえば、Linux のプロセスは、メモリまたはファイルのメタデータ管理操作を実行するために最大 12 個のロック (名前変更操作など)、または平均 4 個のロックを取得できます。

残念ながら、このロック モードでは、キューベースのロックの問題が発生します。この問題では、別のロックを待機している別のスレッドによって作成されるトップレベルのロックを取得するために、一部のスレッドがより長く待機する必要があります。たとえば、スレッド t1 が 1 つの操作として 2 つのロック (L1、次に L2) を取得したいと考えており、t2 は L1 での操作のみを再利用したいとします。これらのロック プロトコルは FIFO ベースであるため、t2 が L1 の取得を待機している間に、t1 はキューの最後で L2 を取得する可能性があります。開発者はカーネルにさらに多くのコンテキストを提供できます。t1 がすべてのロックをまとめて取得するか、t1 がすでに保持しているロックを宣言して、次のロック L2 を取得する優先度を高くすることができます。

アプリケーションは、パフォーマンスを向上させるために、システム コール パスまたは一連のタスクに優先順位を付けたい場合があります。開発者は、タスク優先度コンテキストをエンコードし、この情報を影響を受けるロックに渡すことでこれを行うことができます。システム コールの場合、開発者はクリティカル パス上の一連のロックと優先スレッドに関する情報を共有できます。その後、シャッフラー プログラムは、指定されたアプリケーションのロックを待機している他のスレッドよりもこれらのスレッドを優先します。

3.1.2 スケジューラのセマンティクスの公開

一般に、CPU やメモリなどのハードウェア リソースをオーバーサブスクライブすると、ユーザー空間のランタイム システムと仮想マシンの両方でリソースの使用率が向上します。オーバーサブスクリプションによりハードウェアの使用率は向上しますが、二重スケジューリングの問題も発生します。ハイパーバイザーは、VM 内のロック ホルダーまたは次のロックとして機能する vCPU をスケジュールできます。ハイパーバイザーは、vCPU スケジューリング情報をシャッラー プログラムに公開して、実行時クォータに基づいてサービスに優先順位を付けることができます。

3.1.3 適応型ストップ/ウェイク戦略

すべての閉じたロックは、回転後に駐車する戦略に従います。つまり、一定時間回転した後、自動的に停止します。このスピン時間はほとんどがアドホックです。つまり、ウェイターは時間割り当てに従って一定時間スピンするか、実行するタスクがない場合はスピンし続けます。アプリケーション開発者は、クリティカルセクションの長さを分析した後に時間コンテキストを公開してエネルギー消費を最小限に抑え、ウェイクアップ情報を公開して、次のウェイターを時間通りにスケジュールしてウェイクアップの待ち時間を最小限に抑えることができるようになりました。さらに、開発者は睡眠情報をさらにエンコードして、ロックする前にウェイターを目覚めさせ、長いウェイクアップ遅延を減らすことができます。このアプローチは準仮想化スピンロックでも機能し、コンボイ効果を回避します。

3.2 占有スケジュールの設定

3.2.1 優先度の継承

優先度の逆転は、ロックを保持している優先度の低いタスクが、同じロックを待機するように通常の優先度のタスクによってスケジュールされている場合に発生します。この問題は Linux IO スタックで説明されています。IO リクエストをスケジュールするときに、ロックを取得したい通常のタスクが、同じロックを保持する優先度の低いバックグラウンド タスクをスケジュールする可能性があります。ロックのスケジュール設定はバックグラウンド タスクであるため、IO パフォーマンスの低下につながります。

3.2.2 タスクの公平性のための協調スケジューリング

これにより、2 つのタスクが異なるタイミングでロックを取得する、スケジューラ破壊問題として知られる新しい種類の問題が発生します。タスクが長期間保持されると、オペレーティング システムのスケジュール目標が損なわれます。オペレーティング システムは、重要な領域のサイズを追跡し、長時間実行されるタスクにペナルティを与えることで、この問題を解決します。このソリューションは問題を解決しますが、その恩恵を受けない可能性のあるアプリケーションに対してもスケジューリングの公平性を強制します。

3.2.3 非対称マルチプロセッサ (AMP) マシンでのタスク フェア ロック

1 つのプロセッサー内に異なる計算能力のコアがあるため、このアーキテクチャで使用される基本的なロック プリミティブは、弱いコアの遅い計算能力によりアプリケーションのスループットが崩壊する可能性があるスケジューラ破壊問題に悩まされます。プロセスを高速化するために、開発者は、より高速なコアに重要なロックを割り当てるか、ロックの取得を待機しているスレッドのキューを並べ替えて全体のロックを向上させることができます。

3.2.4 リアルタイムのスケジューリング

リアルタイム システムのスケジューリングと同様に、アプリケーション開発者は、SLO を保証するために常にスレッドをスケジュールするロック ポリシーを作成できます。ここで、ロックは位相公平性に基づくアルゴリズムとして設計でき、このアプローチによりジッターを排除し、遅延が重要なアプリケーションのテール遅延の上限を保証することもできます。

3.3 動的ロックの解析

アプリケーション開発者は、カーネル ロックに関するファイル情報を構成できます。構成するロックを選択すると、開発者はさまざまな粒度レベルで構成できるようになります。たとえば、カーネルで実行されているすべてのスピンロック、特定の関数のロック、コード パスまたは名前空間、さらには個々のロック インスタンスを構成できます。このアプローチは、関心のある部分のみを分析することで、基礎となる同期をより深く理解できるため、アプリケーション開発者にメリットをもたらします。

開発者は、さまざまなシャッフラー ポリシーやポリシーのセットによって提供される特定の保証に基づいて、アプリケーションのパフォーマンスに影響を与えるパフォーマンス契約について推論することもできます。

4. カーネルロックの最適化フレームワーク

カーネル ロックで使用される決定と動作を再定義し、API として公開します。公開された API はユーザー定義のコードに置き換えられ、ユーザーはニーズに応じてロック機能をカスタマイズできます。たとえば、待機キューに参加する前にスピンするかどうかを API にして、ユーザーがその決定を行えるようにすることができます。ユーザーはまず独自のコードを作成して、ユースケースに応じてカーネル内のロック プロトコルを変更し、次にオペレーティング システムがカーネル内の注釈付きロック関数を置き換えます。フロー図は次のとおりです。

2fd18abff7b3d53a0c78a0c8f97412c0.jpeg

ユーザーがロック ポリシーを指定すると (1)、eBPF 検証ツールはコンパイル後に、eBPF 制限と相互排他セキュリティ プロパティを考慮してそれを検証します (2、3)。検証者は検証結果をユーザーに通知し (4)、成功した場合はコンパイルされた eBPF コードをファイル システムに保存します (5)。最後に、ロック (6) を指定するアノテーション付き関数をフィールド パッチ モジュールに置き換えます。

4.1 API

さまざまな API は、セキュリティを確保しながらロック ポリシーの柔軟な実装をサポートしており、オペレーティング システムの基盤となる実装は、eBPF に依存してカーネル ロックを変更します。eBPF とロック API を使用すると、カーネル内の一連のロック インスタンスに対して必要なポリシーが実装されます。ユーザーは複数のポリシーをエンコードでき、これらのポリシーはネイティブ コードに変換され、eBPF 検証ツールによってセキュリティがチェックされます。ベリファイアは、メモリ アクセス制御やホワイトリストに登録されたヘルパー関数のみなど、ネイティブ コードをカーネルにロードする前にシンボリック実行を実行します。

a3dc3db3218f5db4a677093b2e205839.jpeg

4.2 セキュリティ

eBPF バリデータに加えて、ShflLocks にはロック取得のための個別のフェーズと待機キューを並べ替えるためのフェーズがあります。ユーザーは、API 関数を利用して現在のノードとシャフラー ノードを比較し、現在のノードを並べ替えるかどうかを判断できます。また、クリティカル スライス長が短いノードを優先するスケジューラー協調ロックを設計して、ノードの優先順位を下げることもできます。クラス。

ユーザー実装が正しくない場合、公平性が保証されたポリシーが破られる可能性がありますが、実行時に mutex プロパティをチェックして保証することができます。また、カーネルにはデッドロックの問題はなく、API はロックの動作を変更せず、ノードを移動する決定を返すだけです。開発者は、各呼び出しに必要な動作を実装することで、きめ細かい方法でロックを構成できます。ロック関数の動作は変更されませんが、重みプロファイル分析戦略によりクリティカル セクションの長さが増加し、パフォーマンスの低下を引き起こす可能性があります。

さらに、eBPF は、ユーザーがポリシーを作成するために使用できる複数の eBPF プログラムをチェーンする機能を公開します。最後に、ロック プリミティブによって使用されるデータ構造を変更するために、フィールド ディスパッチ データ構造にも依存します。たとえば、キュ​​ーベースのロック ノード データ構造は、特定の使用例に合わせて情報をエンコードするために使用される追加情報で拡張できます。最悪の場合、ユーザー空間コードを実行しない場合、ロック アルゴリズムを動的に変更すると、最大 20% のオーバーヘッドが発生する可能性があります。

4.3 組み合わせ戦略

カーネルの同時実行制御を調整することにより、アプリケーションはソフトウェア スタックをより詳細に制御できるようになります。アプリケーション開発者は、アプリケーションのロックを必要とする一連のポリシーを提供します。複数の戦略を組み合わせるのは、特にいくつかの戦略が競合する可能性がある場合には困難な作業です。プログラム構成を活用してこのプロセスを自動化すると、セキュリティ プロパティをユーザー空間での検証に完全に移行できる可能性があり、また、矛盾するポリシーを構成する安全な方法も提供されます。

実行がクリティカル パスに該当する可能性があるため、ユーザーはポリシーを追加しすぎることはできません。1 人の特権ユーザーがカーネル ロックを変更できるようにします。このモデルは、システム全体を使用する 1 人のユーザーのみを対象としています。ただし、クラウド環境でマルチテナンシーを処理するには、ユーザー間の分離に違反しないテナント対応のポリシー作成者が必要です。このような競合を回避するためにユーザー空間でポリシーを合成し、ポリシーが特定の動作に影響を与える可能性がある場合にのみ使用されるロック アルゴリズムに実行時チェックを追加します。

ロックに加えて、RCU、seqlock、待機イベント、アプリケーションのパフォーマンスをさらに向上させるその他の拡張機能など、カーネルで頻繁に使用される同期メカニズムがあります。そうは言っても、ユーザー空間アプリケーションには、本質的に汎用的な独自のロックもあります。対照的に、ライブラリ補間などの既存の手法では、アプリケーションの実行開始時に、さまざまなロック実装に対して 1 つの変更しか許可されません。

5. まとめ

カーネルロック同期プリミティブは、一部のアプリケーションのパフォーマンスとスケーラビリティに大きな影響を与えますが、カーネル同期プリミティブの制御はアプリケーション開発者にとって手の届かないところにあります。コンテキスト同時実行制御を使用すると、ユーザー空間アプリケーションでカーネル同時実行プリミティブを微調整できるようになります。これはソフトウェア スタックの専門化に関する考え方であり、コア同期の分野でのイノベーションを部分的に加速します。

【参考資料と関連書籍】

おすすめ

転載: blog.csdn.net/wireless_com/article/details/131160180