Linux パフォーマンス最適化の実践 - CPU コンテキスト

CPUコンテキストスイッチ

Linux はマルチタスク オペレーティング システムであり、CPU の数をはるかに超えるタスクの同時実行をサポートします。これらのタスクは本当の意味で並行して実行されるわけではありませんが、システムは短時間で順番に CPU を割り当てるため、タスクが同時に実行されているように見えます。
CPU は、タスクがどこからロードされるかを知る必要があり、どこから実行を開始するかは、設定​​された CPU レジスタとプログラム カウンター (プログラム カウンター、PC) によって行われます。

  • CPU レジスタ: 小容量ながら非常に高速な CPU 内蔵メモリ。
  • プログラム カウンタ: CPU によって実行されている命令の位置、または次に実行される命令の位置を保存するために使用されます。これらはすべて、CPU がタスクを実行する前に実行する必要がある依存環境であるため、CPU コンテキストとも呼ばれます。
    ここに画像の説明を挿入
    CPU コンテキストの切り替えでは、まず前のタスクの CPU コンテキスト (CPU レジスタとプログラム カウンタ) を保存し、次に新しいタスクのコンテキストをこれらのレジスタとプログラム カウンタにロードし、最後にプログラム カウンタが指す新しい位置にジャンプします。新しいタスクを実行します。
    プロセスとスレッドは最も一般的なタスクであり、さらに、割り込みハンドラーの呼び出しを引き起こすハードウェア信号もあり、これも一般的なタスクです。
    さまざまなタスクに応じて、CPU コンテキストの切り替えは、プロセス コンテキストの切り替え、スレッド コンテキストの切り替え、および割り込みコンテキストの切り替えといういくつかの異なるシナリオに分割できます。

プロセスコンテキストスイッチ。

Linux は特権レベルに応じてプロセスの実行領域をカーネル領域とユーザー領域に分割し、CPU 特権レベルはリング 0 とリング 3 です。

  • カーネル空間 (リング 0) は最高の権限を持ち、すべてのリソースに直接アクセスできます。
  • ユーザー空間 (リング 3) は制限されたリソースのみにアクセスでき、メモリなどのハードウェア デバイスに直接アクセスすることはできません。これらの特権リソースにアクセスするには、システム コールを通じてカーネル内にトラップする必要があります。
    ここに画像の説明を挿入
    プロセスはユーザー空間とカーネル空間の両方で実行できます。プロセスがユーザー空間で実行される場合、プロセスのユーザー モードと呼ばれ、プロセスがカーネル空間に分類される場合、プロセスのカーネル モードと呼ばれます。

ユーザー モードからカーネル モードへの移行は、システム コールを通じて行う必要があります。たとえば、ファイルの内容を表示する場合、複数のシステム コールを完了する必要があります。最初に open() を呼び出してファイルを開き、次に read() を呼び出してファイルの内容を読み取り、write() を呼び出して内容を書き込みます。標準出力に出力し、最後に close() を呼び出してファイルを閉じます。

  1. システムコールの過程で CPU コンテキストの切り替えが行われましたか?

答えはもちろん「はい」です。

CPU レジスタ内の元のユーザー モード命令の位置を最初に保存する必要があります。次に、カーネル モード コードを実行するには、CPU レジスタをカーネル モード命令の新しい位置で更新する必要があります。最後に、カーネル モードにジャンプしてカーネル タスクを実行します。システムコールが終了した後、CPU レジスタは元の保存されたユーザー状態を復元し、ユーザー空間に切り替えてプロセスの実行を継続する必要があります。したがって、システム コールのプロセスでは、実際には 2 つの CPU コンテキスト スイッチが存在します。

システムコールの処理中に、仮想メモリなどのプロセスのユーザー状態リソースが関与したり、プロセスが切り替わったりすることはありません。これは、通常プロセス コンテキストの切り替えと呼ばれるものとは異なります。

  • プロセス コンテキストの切り替えとは、あるプロセスから別のプロセスに切り替えることを指します。
  • システムコール中は常に同じ処理が実行される
    ため、システムコール処理は通常コンテキストスイッチではなく特権モードスイッチと呼ばれますしかし実際には、システムコールの処理中に CPU のコンテキストスイッチが依然として避けられません。
  1. では、プロセス コンテキスト スイッチとシステム コールの違いは何でしょうか?

まず、プロセスはカーネルによって管理およびスケジュールされ、プロセスの切り替えはカーネル状態でのみ発生します。したがって、プロセスのコンテキストには、仮想メモリ、スタック、グローバル変数などのユーザー空間リソースだけでなく、カーネル スタックやレジスタなどのカーネル空間の状態も含まれます。

したがって、プロセスのコンテキスト スイッチはシステム コールよりも 1 つ多くのステップです: 現在のプロセスのカーネル状態と CPU レジスタを保存する前に、プロセスの仮想メモリ、スタックなどを保存する必要があり、カーネル状態も保存する必要があります。次のプロセスのロードが行われた後、プロセスの仮想メモリとユーザー スタックをリフレッシュする必要があります。

以下の図に示すように、コンテキストの保存と復元のプロセスは「無料」ではなく、完了するには CPU 上でカーネルを実行する必要があり、各コンテキストの切り替えには数十ナノ秒から数マイクロ秒の CPU 時間が必要です
ここに画像の説明を挿入
この時間は依然としてかなり長く、特に多数のプロセス コンテキストの切り替えの場合、CPU はレジスタ、カーネル スタック、仮想メモリなどのリソースの保存と復元に多くの時間を費やしやすくなります。実際の実行時間、つまり処理にかかる時間を大幅に短縮します。これはまさに前のセクションで述べたことであり、平均負荷の増加につながる重要な要素です。

Linux は、TLB (Translation Lookaside Buffer) を使用して、仮想メモリと物理メモリ間のマッピング関係を管理します。仮想メモリが更新されるとTLBも更新する必要があり、メモリアクセスも遅くなります。特にマルチプロセッサ システムでは、キャッシュは複数のプロセッサで共有されるため、キャッシュを更新すると、現在のプロセッサのプロセスだけでなく、キャッシュを共有する他のプロセッサのプロセスにも影響します。

  1. プロセスコンテキストはいつ切り替わりますか?

コンテキストを切り替える必要があるのは、プロセスが切り替わるときだけ、つまり、プロセスがスケジュールされたときだけコンテキストを切り替える必要があります。Linux は各 CPU の準備完了キューを維持し、優先順位と CPU の待機時間に従ってアクティブなプロセス (つまり、実行中で CPU を待っているプロセス) を並べ替えて、最も CPU を必要とするプロセスを選択します。優先度が最も高いプロセスを選択し、CPU 時間が最も長いプロセスが実行されるまで待機します。

  1. プロセスはいつ CPU 上で実行されるようにスケジュールされますか?

つまり、プロセスは実行後に終了し、それまで使用していたCPUが解放され、その際にレディキューから新しいプロセスが取得されて実行されます。実際には、プロセス スケジューリングをトリガーするシナリオは他にもたくさんあります。ここでは、それらを 1 つずつ整理していきます。

まず、すべてのプロセスを公平にスケジュールできるようにするために、CPU 時間がタイム スライスに分割され、これらのタイム スライスが各プロセスに順番に割り当てられます。このようにして、特定のプロセスのタイム スライスが使い果たされると、そのプロセスはシステムによって一時停止され、CPU を待機している他のプロセスによって実行されるように切り替えられます。

次に、システム リソースが不足している場合 (メモリ不足など)、リソースが満たされるまでプロセスは実行できません。このとき、プロセスも一時停止され、システムは他のプロセスの実行をスケジュールします。

3 番目に、スリープ関数などの方法でプロセスが積極的に一時停止されると、自然にスケジュールが変更されます。

第 4 に、優先度の高いプロセスが実行されている場合、優先度の高いプロセスの実行を確実にするために、現在のプロセスは一時停止され、優先度の高いプロセスによって実行されます。

第 5 に、ハードウェア割り込みが発生すると、CPU 上のプロセスは割り込みによって一時停止され、カーネル内で割り込みサービス プログラムが実行されます。

スレッドコンテキストスイッチ

スレッドとプロセスの最大の違いは、スレッドがスケジューリングの基本単位であるのに対し、プロセスはリソース所有権の基本単位であることです

カーネル内のいわゆるタスク スケジューリングは、実際にスレッドをスケジュールします。プロセスは、仮想メモリやグローバル変数などのリソースをスレッドに提供するだけです。スレッドとプロセスは次のように理解できます。

プロセスにスレッドが 1 つしかない場合、プロセスとスレッドは等しいと考えることができます。

プロセスに複数のスレッドがある場合、これらのスレッドは仮想メモリやグローバル変数などの同じリソースを共有します。これらのリソースは、コンテキストの切り替え中に変更する必要はありません。

さらに、スレッドにはスタックやレジスタなどの独自のプライベート データもあり、コンテキストの切り替え中にこれらも保存する必要があります。

スレッド コンテキストの切り替えは、実際には次の 2 つの状況に分類できます。

まず、前後の 2 つのスレッドは異なるプロセスに属しています。このとき、リソースは共有されないため、切り替え処理はプロセスコンテキストの切り替えと同じになります。

次に、前後の 2 つのスレッドは同じプロセスに属します。このとき、仮想メモリが共有されているため、切り替え時に仮想メモリなどのリソースは変化せず、プライベートデータやスレッドのレジスタなど共有されていないデータのみを切り替える必要があります。

同じプロセス内でのスレッドの切り替えは、複数のプロセス間で切り替えるよりも消費するリソースが少なくなります。これは、マルチプロセスではなくマルチスレッドの利点でもあります。

割り込みコンテキストスイッチ

ハードウェア イベントに迅速に応答するために、割り込み処理は通常のスケジューリングとプロセスの実行を中断し、代わりに割り込みハンドラーを呼び出してデバイス イベントに応答します。他のプロセスを中断する場合、中断が終了した後もプロセスを元の状態から再開できるように、プロセスの現在の状態を保存する必要があります。

プロセス コンテキストとは異なり、割り込みコンテキストの切り替えにはプロセスのユーザー モードが関与しません。そのため、割り込み処理がユーザーモードのプロセスに割り込んだ場合でも、そのプロセスの仮想メモリやグローバル変数などのユーザーモードのリソースを退避・復元する必要がありません。

実際には、割り込みコンテキストには、CPU レジスタ、カーネル スタック、ハードウェア割り込みパラメータなど、カーネル モード割り込みサービス プログラムの実行に必要な状態のみが含まれます。同じ CPU の場合、割り込み処理の優先順位は process よりも高いため、割り込みコンテキストの切り替えはプロセス コンテキストの切り替えと同時に発生しません。同様に、割り込みは通常のプロセスのスケジューリングと実行を中断するため、実行ができるだけ早く終了できるように、ほとんどの割り込みハンドラーは短くて簡潔です。さらに、プロセス コンテキストの切り替えと同様に、割り込みコンテキストの切り替えも CPU を消費します。切り替え回数が多すぎると大量の CPU を消費し、システム全体のパフォーマンスが大幅に低下することさえあります。したがって、割り込みが多すぎることが判明した場合は、それがシステムに重大なパフォーマンス上の問題を引き起こすかどうかに注意する必要があります。

システムのコンテキストスイッチングステータスを確認する方法

vmstat を使用します (vmstat コマンドは、最も一般的な Linux/Unix 監視ツールであり、サーバーの CPU 使用率、メモリ使用量、仮想メモリ スワップ、IO 読み取りおよび書き込みなど、サーバーのステータス値を指定された時間間隔で表示できます) このツール, システムのコンテキスト切り替え状況を問い合わせるには、

vmstat のインストール
apt-get を手動でインストールする方法Mac OS に
vmstat コレクターをインストールする

vmstat 5このコマンドは 5 秒ごとに一連のデータを出力することを示しており、data パラメータ項目の意味は次のとおりです。

  • r (実行中または実行可能): レディキューの長さ、つまり、実行中および CPU を待機しているプロセスの数。
  • b (ブロック): 無中断スリープ状態にあるプロセスの数。
  • swpd (スワップ デーモン): 仮想メモリ サイズを使用した、1 秒あたりのディスクからメモリへのスワップ数。
  • free: 空き物理メモリのサイズ。
  • バッファ: デバイスとデバイス間のバッファのサイズ。
  • キャッシュ: CPU とメモリ間のバッファ サイズ。
  • si (スワップ入力): 1 秒あたりにディスクから読み取られる仮想メモリのサイズ。この値が 0 より大きい場合、物理メモリが不足しているか、メモリ リークが発生していることを意味します。メモリを見つける必要があります。プロセスを消費して解決する。
  • so (スワップ出力): 1 秒あたりにディスクに書き込まれる仮想メモリのサイズ。この値が 0 より大きい場合、仮想メモリが十分ではないか、メモリ リークが発生していることを意味します。
  • bi (ブロック入力): ブロック デバイスが 1 秒あたりに受信したブロックの数。ここでのブロック デバイスとは、システム上のすべてのディスクおよびその他のブロック デバイスを指します。デフォルトのブロック サイズは 1024 バイトです。IO 操作がない場合は、常に 0 になります。
  • bo (ブロック出力): ブロック デバイスによって 1 秒あたりに送信されるブロックの数 たとえば、ファイルを読み取る場合、bo は 0 より大きくなければなりません。bi と bo は通常 0 に近く、それ以外の場合は IO の頻度が高すぎるため、調整する必要があります。
  • in (割り込み): 1 秒あたりの割り込み数。
  • cs (コンテキストスイッチ): 1 秒あたりのコンテキストスイッチの数。
  • us (ユーザー時間): ユーザーの CPU 時間。
  • sy (システム時間): システムの CPU 時間。高すぎる場合は、頻繁な IO 操作など、システム呼び出し時間が長いことを意味します。
  • id (アイドル時間): アイドル CPU 時間。一般的に、id + us + sy = 100、id はアイドル CPU 使用率、us はユーザー CPU 使用率、sy はシステム CPU 使用率です。
  • wa (待機時間): IO CPU 時間を待機します。
  • st (スチール時間): 仮想 CPU が物理 CPU を待機する時間。

vmstat はシステム全体のコンテキスト切り替え状況を示しますが、各プロセスの詳細を表示したい場合は pidstat を使用する必要があります。
コマンドを入力して、pidstat -w 5各プロセスのコンテキスト切り替えを表示します。

  • cswch: 1 秒あたりの自発的なコンテキスト切り替えの数を示します。
  • nvcswch: 1 秒あたりの非自発的コンテキスト切り替えの数を示します。

いわゆる自発的コンテキスト切り替えとは、プロセスが必要なリソースを取得できないことによって引き起こされるコンテキスト切り替えを指しますたとえば、I/O やメモリなどのシステム リソースが不足している場合、自発的なコンテキスト切り替えが発生します。非自発的なコンテキスト スイッチングとは、タイム スライスの有効期限が切れたなどの理由により、プロセスがシステムによって強制的にスケジュールされたために発生するコンテキスト スイッチングを指しますたとえば、多数のプロセスが CPU をめぐって競合している場合、非自発的なコンテキスト スイッチが発生する傾向があります。

つづく

参考リンク:
1. https://zhuanlan.zhihu.com/p/406497025

おすすめ

転載: blog.csdn.net/zkkzpp258/article/details/131621204