(1)2つの異なるプロセス間の、および(2)同じプロセス内の2つの異なるスレッド間のコンテキストスイッチに関連する手順について説明するように求められます。
これは一般的すぎますか、それともプロセスをより明確に説明するために何を追加しますか?
プロセスの切り替えには常にスレッドの切り替えが含まれるため、これらを逆の順序で説明する方がはるかに簡単です。
シングルコアCPUでの一般的なスレッドコンテキストスイッチは、次のように行われます。
すべてのコンテキスト切り替えは「割り込み」によって開始されます。これは、ドライバー(ネットワークカード、キーボード、メモリ管理、タイマーハードウェアなど)を実行する実際のハードウェア割り込み、またはハードウェア割り込みのような呼び出しシーケンスを実行するソフトウェア呼び出し(システムコール)の可能性があります。 OSに入ります。ドライバー割り込みの場合、OSは、「通常の」直接割り込み戻りを実行する代わりにドライバーが呼び出すことができるエントリポイントを提供します。これにより、OSがスレッドを設定する必要がある場合、ドライバーはOSスケジューラを介して終了できます。準備ができている(たとえば、セマフォに信号を送った)。
重要なシステムでは、カーネルコード/データなどにアクセスできるように、ハードウェア保護レベルの変更を開始してカーネル状態にする必要があります。
中断されたスレッドのコア状態を保存する必要があります。単純な組み込みシステムでは、これはすべてのレジスターをスレッドスタックにプッシュし、スタックポインターをそのスレッド制御ブロック(TCB)に保存するだけの場合があります。
多くのシステムはこの段階でOS専用スタックに切り替わるため、OSの内部スタック要件の大部分がすべてのスレッドのスタックに影響を与えることはありません。
ネストされた割り込みを可能にするために、割り込み状態への変更が発生したスレッドスタックの位置をマークする必要がある場合があります。
ドライバー/システムコールが実行され、さまざまなスレッドの優先順位の内部キューからTCBを追加/削除することにより、準備ができたスレッドのセットを変更できます。ネットワークカードドライバがイベントを設定したか、セマフォに別のスレッドが待機していたことを通知して、スレッドがレディセットに追加されるか、実行中のスレッドがsleep()を呼び出し、レディセットから自分自身を削除することを選択した可能性があります。 。
OSスケジューラアルゴリズムは、次に実行するスレッドを決定するために実行されます。通常、その優先度のキューの先頭にある、最も優先度の高い準備ができたスレッドです。次に実行されるスレッドが、以前に実行されたスレッドとは異なるプロセスに属している場合は、ここで追加のものが必要になります(後述)。
そのスレッドのTCBから保存されたスタックポインターが取得され、ハードウェアスタックポインターに読み込まれます。
選択したスレッドのコア状態が復元されます。私の単純なシステムでは、選択したスレッドのスタックからレジスターがポップされます。より複雑なシステムでは、ユーザーレベルの保護への復帰を処理する必要があります。
割り込み戻りが実行されるため、選択したスレッドに実行が転送されます。
マルチコアCPUの場合、状況はより複雑になります。スケジューラは、別のコアで現在実行中のスレッドを停止して、準備ができたばかりのスレッドに置き換える必要があると判断する場合があります。インタープロセッサドライバを使用して、停止する必要のあるスレッドを実行しているコアをハードウェアで中断することにより、これを行うことができます。他のすべてのものに加えて、この操作の複雑さは、OSカーネルの作成を回避するための十分な理由です:)
典型的なプロセスコンテキストスイッチは次のように行われます。
プロセスコンテキストスイッチはスレッドコンテキストスイッチによって開始されるため、上記の1〜9のすべてが発生する必要があります。
上記のステップ5で、スケジューラは、以前に実行していたスレッドを所有していたプロセスとは異なるプロセスに属するスレッドを実行することを決定します。
メモリ管理ハードウェアには、新しいプロセスのアドレス空間、つまり、新しいプロセスのスレッドにメモリへのアクセスを許可するセレクタ/セグメント/フラグ/何でもロードする必要があります。
FPUハードウェアのコンテキストは、PCBから保存/復元する必要があります。
保存/復元が必要な他のプロセス専用ハードウェアがある場合があります。
実際のシステムでは、メカニズムはアーキテクチャに依存しており、上記はいずれかのコンテキストスイッチの影響に関する大まかな不完全なガイドです。厳密にはスイッチの一部ではない、プロセススイッチによって生成される他のオーバーヘッドがあります。メモリの一部がページに属しているためにページアウトされている可能性があるため、プロセススイッチ後に余分なキャッシュフラッシュやページフォールトが発生する可能性があります。以前に実行されていたスレッドを所有するプロセスに。
より詳細で明確な画像を提供できることを願っています。
まず、OSはプロセスではなくスレッドをスケジュールします。これは、システム内で実行可能なユニットがスレッドだけだからです。プロセス切り替えは、スレッドが異なるプロセスに属している単なるスレッド切り替えであり、したがって、手順は基本的に同じです。
スケジューラーが呼び出されます。これが発生する可能性のある3つの基本的なシナリオがあります。
すべての場合において、コンテキスト切り替えを実行できるようにするには、制御をカーネルに渡す必要があります。非自発的スイッチの場合、これは割り込みによって実行されます。自発的(および半自発的)なコンテキストスイッチの場合、制御はシステムコールを介してカーネルに渡されます。
どちらの場合も、カーネルエントリはCPU支援です。プロセッサは、権限チェックを実行し、命令ポインタを保存し(後で正しい命令から実行を継続できるようにする)、ユーザーユーザーモードからカーネルモードに切り替え、カーネルスタック(現在のスレッドに固有)をアクティブにし、事前定義された場所にジャンプしますカーネルコードのよく知られたポイント。
カーネルによって実行される最初のアクションは、CPUレジスタの内容を保存することです。CPUレジスタは、それ自体の目的で使用する必要があります。通常、カーネルは汎用CPUレジスタのみを使用し、それらをスタックにプッシュして保存します。
その後、カーネルは必要に応じてプライマリリクエストを処理します。割り込みの処理、ファイルの読み取り要求の準備、タイマーのリロードなどが可能です。
リクエスト処理中のある時点で、カーネルは現在のスレッド(このスレッドで何かを待機しているため、現在何もする必要がない)の状態または別のスレッド(またはスレッド)の状態に影響を与えるアクションを実行します(スレッドは、それが待機していたイベントが発生したために実行可能になった-たとえば、ミューテックスが解放された)。
カーネルがスケジューラーを呼び出します。スケジューラは2つの決定を行わなければなりません。
両方の決定が行われると、スケジューラは、現在のスレッドのTCBと次に実行されるスレッドのTCBを使用してコンテキストスイッチを実行します。
コンテキストスイッチ自体は、3つの主要なステップで構成されます。
この時点で、カーネルは、スケジュールされたスレッドとスケジュールされていないスレッドが同じプロセスに属しているかどうかをチェックします。そうでない場合(「スレッド」スイッチではなく「プロセス」)、カーネルはMMU(メモリ管理ユニット)をスケジュールされたプロセスのページテーブルにポイントすることにより、現在のアドレス空間をリセットします。TLB (Translation Lookaside Buffer)は、最近の仮想アドレスから物理アドレスへの変換を含むキャッシュであり、誤ったアドレス変換を防ぐためにフラッシュされます。これは、プロセスを対象とするコンテキスト切り替えアクションのセット全体での唯一のステップです。
カーネルは、スケジュールされたスレッド用のスレッドローカルストレージを準備します。たとえば、それぞれのメモリページを指定されたアドレスにマップします。別の例として、IA-32プラットフォームでの一般的なアプローチは、着信スレッドのTLSデータを指す新しいセグメントをロードすることです。
カーネルは、現在のスレッドのカーネルスタックアドレスをCPUにロードします。この後、すべてのカーネル呼び出しは、スケジュールされていないスレッドのカーネルスタックではなく、このカーネルスタックを使用します。
カーネルが実行できる別のステップは、システムタイマーの再プログラミングです。タイマーが作動すると、制御がカーネルに戻ります。コンテキストの切り替えとタイマーの起動の間の期間はタイムクォンタムと呼ばれ、その時点で現在のスレッドに与えられている実行時間を示します。これはプリエンプティブスケジューリングと呼ばれます。
カーネルは通常、コンテキストの切り替え中に統計を収集して、スケジューリングを改善するとともに、システム管理者とユーザーにシステムで何が起こっているかを示します。これらの統計には、スレッドが消費したCPU時間、スケジュールされた回数、タイムクォンタムの有効期限が切れた回数、システムでコンテキストスイッチが発生する頻度などの情報が含まれます。
コンテキストスイッチはこの時点で準備ができていると見なすことができ、カーネルは以前に中断されたシステムアクションを続行します。たとえば、システムコール中にスレッドがミューテックスを取得しようとしたときにミューテックスが解放された場合、カーネルは中断された操作を終了する可能性があります。
ある時点で、スレッドはシステムアクティビティを終了し、非システムコードを実行するためにユーザーモードに戻りたいと考えています。カーネルは、カーネルエントリ時に以前に保存された汎用レジスタのカーネルスタックコンテンツからポップし、CPUにユーザーモードに戻るための特別な命令を実行させます。
CPUは、以前に保存されたカーネルモードに入った命令ポインターとスタックポインターの値をキャプチャし、それらを復元します。この時点で、スレッドのユーザーモードスタックもアクティブになり、カーネルモードが終了します(これにより、特別なシステム命令の使用が禁止されます)。
最後に、CPUは、スケジュールされていないスレッドの時点から実行を継続します。システムコール中に発生した場合、スレッドはその結果をキャプチャして処理することにより、システムコールが呼び出されたポイントから処理を進めます。割り込みによる横取りの場合、スレッドは何も起こらなかったかのように実行を継続します。
いくつかの要約ノート:
カーネルは、プロセスではなくスレッドのみをスケジュールして実行します。コンテキストの切り替えはスレッド間で行われます。
別のプロセスからスレッドのコンテキストに切り替える手順は、同じプロセスに属するスレッド間のコンテキスト切り替えでも基本的に同じです。追加する必要があるのは、ページテーブルの変更(およびTLBのフラッシュ)だけです。
スレッドコンテキストは、カーネルスタックまたはTCB(PCBではない!)に格納されます。
コンテキストスイッチングは負荷の高い操作です。パフォーマンスにかなりの直接的なコストがかかり、キャッシュの汚染(およびプロセス間で切り替えが発生した場合はTLBフラッシュ)によって発生する間接的なコストはさらに大きくなります。
(ソース: コンテキストスイッチ )
1.現在CPUで実行されているプロセスのコンテキストを保存します。プロセス制御ブロックおよびその他の重要なフィールドを更新します。
2.上記のプロセスのプロセス制御ブロックを、レディキュー、I/Oキューなどの関連するキューに移動します。
3.実行する新しいプロセスを選択します。
4.選択したプロセスのプロセス制御ブロックを更新します。これには、プロセスの状態を実行中に更新することが含まれます。
5.必要に応じて、メモリ管理データ構造を更新します。
6.プロセッサに再度読み込まれたときに、以前に実行されていたプロセスのコンテキストを復元します。これは、プロセス制御ブロックとレジスタの以前の値をロードすることによって行われます。