1. ホーム
  2. linux-kernel

コンテキストスイッチの内部構造

2023-11-14 06:15:45

質問

この質問を参考に勉強して、知識のギャップを埋めたいのですが。

つまり、ユーザーがスレッド(カーネルレベル)を実行しており、それが今、以下を呼び出します。 yield (を呼び出したとします(システムコールと推測されます)。 スケジューラは、現在のスレッドのコンテキストを TCB (カーネルのどこかに保存されている) に保存し、実行する別のスレッドを選択して、そのコンテキストをロードし、その CS:EIP . 物事を絞り込むために、私はx86アーキテクチャの上で動作するLinuxで作業しています。さて、詳細に入りたいと思います。

まず、システムコールがありますね。

1) ラッパー関数で yield は、システムコールの引数をスタックにプッシュします。リターンアドレスをプッシュし、何らかのレジスタにプッシュされたシステムコール番号で割り込みを発生させる (例えば EAX ).

2) 割り込みによりCPUのモードがユーザからカーネルに変わり、割り込みベクタテーブルにジャンプし、そこからカーネル内の実際のシステムコールに移行します。

3) スケジューラが呼び出され、今度はTCBに現在の状態を保存しなければならないのでしょう。ここで、私のジレンマがあります。スケジューラはその操作を実行するためにユーザースタックではなくカーネルスタックを使用するので (これはつまり SSSP を変更する必要があります) プロセス中のレジスタを変更することなく、どのようにしてユーザーの状態を保存するのでしょうか。フォーラムで、状態を保存するための特別なハードウェア命令があることを読みましたが、それではスケジューラはどのようにしてそれらにアクセスし、誰がいつこれらの命令を実行するのでしょうか?

4) スケジューラは今、状態をTCBに保存し、別のTCBをロードしています。

5) スケジューラが元のスレッドを実行すると、制御がラッパー関数に戻り、スタックをクリアしてスレッドが再開されます。

サイドの質問です。スケジューラーはカーネル専用スレッド (すなわち、カーネル コードのみを実行できるスレッド) として実行されるのですか? 各カーネル スレッドまたは各プロセスに個別のカーネル スタックがあるのでしょうか?

どのように解決するのですか?

高いレベルでは、理解すべき 2 つの別々のメカニズムがあります。 1 つは、カーネルの入口/出口メカニズムです。これは、1 つの実行中のスレッドを、ユーザー モード コードの実行からそのスレッドのコンテキストでのカーネル コードの実行に切り替え、また元に戻します。 2 つ目は、コンテキスト スイッチのメカニズム自体で、カーネル モードで 1 つのスレッドのコンテキストでの実行から別のスレッドへの切り替えを行います。

ですから、スレッド A が sched_yield() を呼び出し、スレッドBに置き換えた場合、何が起こるかというと。

  1. スレッド A はカーネルに入り、ユーザー モードからカーネル モードに変更します。
  2. カーネル内のスレッド A がカーネル内のスレッド B にコンテキスト スウィッチします。
  3. スレッド B はカーネルを終了し、カーネル モードからユーザー モードに戻ります。

各ユーザー スレッドには、ユーザー モード スタックとカーネル モード スタックの両方があります。 スレッドがカーネルに入ると、ユーザーモードスタックの現在値( SS:ESP ) と命令ポインタ ( CS:EIP ) がスレッドのカーネルモードスタックに保存され、CPU はカーネルモードスタックに切り替わる - このとき int $80 syscall メカニズムでは、これは CPU 自身によって行われます。 残りのレジスタ値とフラグもカーネル スタックに保存されます。

スレッドがカーネルからユーザーモードに戻るとき、レジスタ値とフラグはカーネルモードスタックからポップされ、ユーザーモードスタックと命令ポインタの値はカーネルモードスタックに保存された値からリストアされます。

スレッドがコンテキストスイッチを行うと、スケジューラが呼び出されます (スケジューラは別のスレッドとして実行されるわけではなく、常に現在のスレッドのコンテキストで実行されます)。 スケジューラのコードは次に実行するプロセスを選択し、そのプロセスに対して switch_to() 関数を呼び出します。 この関数は本質的にカーネルスタックを切り替えるだけで、スタックポインタの現在値を現在のスレッド用の TCB に保存します(これは struct task_struct と呼ばれる) に保存し、次のスレッド用に TCB から以前に保存されたスタックポインタをロードします。 この時点で、カーネルが通常使用しない他のスレッド状態(浮動小数点/SSEレジスタなど)も保存・復元されます。 切り替えられるスレッドが同じ仮想メモリ空間を共有していない場合(つまり、それらは異なるプロセス内にある)、ページテーブルも切り替えられる。

スレッドのコア ユーザー モード状態がコンテキスト スイッチ時に保存および復元されるのではなく、カーネルに入ったり出たりするときにスレッドのカーネル スタックに保存および復元されることがおわかりいただけたと思います。 コンテキスト スイッチのコードでは、ユーザー モード レジスタの値を変更することを心配する必要はなく、その時点ではすでにカーネル スタックに安全に保存されています。