30

Linuxカーネルのスケジュールプロセスが実際にどのように機能するかを理解しようとしています. 私の質問は、スケジューリング アルゴリズムに関するものではありません。その機能schedule()switch_to()働きについてです。

説明してみます。私はそれを観た:

プロセスがタイム スライスを使い果たすと、フラグneed_reschedが によって設定されscheduler_tick()ます。カーネルはフラグをチェックし、フラグが設定されていることを確認し、schedule()(質問 1 に関連する) 新しいプロセスに切り替えるように呼び出します。このフラグは、別のプロセスを実行する必要があるため、できるだけ早くスケジュールを呼び出す必要があるというメッセージです。ユーザー空間に戻るか、割り込みから戻ると、need_reschedフラグがチェックされます。設定されている場合、カーネルは続行する前にスケジューラを呼び出します。

カーネル ソース (linux-2.6.10 - 本「Linux Kernel Development, second edition」の基になっているバージョン) を調べると、一部のコードがschedule()自発的に関数を呼び出して、別のプロセスに実行権を与えることができることもわかりました。この関数switch_to()は、実際にコンテキスト スイッチを実行する関数であることがわかりました。switch_to()実際に何が行われているかを理解しようと、いくつかのアーキテクチャに依存するコードを調べました。

その行動は、私が答えを見つけることができなかったいくつかの質問を提起しました:

  1. 終了したらswitch_to()、現在実行中のプロセスは何ですか? schedule()?を呼び出したプロセス それとも、実行するために選択された次のプロセスですか?

  2. 割り込みによって呼び出されると、実行するように選択されたプロセスは、割り込み処理が終了したときschedule()に実行を開始します (ある種の RTE の後)。それともその前?

  3. schedule()関数が割り込みから呼び出すことができない場合、いつフラグがneed_resched設定されますか?

  4. タイマー割り込みハンドラが動作しているとき、どのスタックが使用されていますか?

私は自分自身を明確にすることができるかどうかわかりません。できなかった場合は、いくつかの回答(または質問)の後にできることを願っています。そのプロセスを理解しようとして、すでにいくつかの情報源を見てきました。私は「Linux Kernel Development, sec ed」という本を持っていて、それも使っています。説明に役立つ場合は、MIP と H8300 アーキテクチャについて少し知っています。

4

1 に答える 1

37
  1. を呼び出した後switch_to()、カーネル スタックは で指定されたタスクのスタックに切り替えられますnext。アドレス空間の変更などは、eg で処理されcontext_switch()ます。
  2. schedule()割り込みからを含め、アトミック コンテキストで呼び出すことはできません ( のチェックを参照schedule_debug())。再スケジュールが必要な場合は、割り込みリターン パスでチェックされる TIF_NEED_RESCHED タスク フラグが設定されます。
  3. 2を参照してください。
  4. デフォルトの 8K スタックでは、割り込みは現在実行中のカーネル スタックで処理されると思います。4K スタックが使用されている場合、別の割り込みスタック (x86 マジックのおかげで自動的に読み込まれる) があると思いますが、その点については完全にはわかりません。

もう少し詳しく説明するために、実際の例を次に示します。

  1. 割り込みが発生します。CPU は、割り込み番号をスタックにプッシュする割り込みトランポリン ルーチンに切り替えてから、common_interruptにジャンプします。
  2. common_interrupt はdo_IRQを呼び出し、プリエンプションを無効にしてから IRQ を処理します。
  3. ある時点で、タスクを切り替える決定が下されます。これは、タイマー割り込みまたはウェイクアップ コールによる可能性があります。いずれの場合も、set_task_need_reschedが呼び出され、TIF_NEED_RESCHED タスク フラグが設定されます。
  4. 最終的に、CPU は元の割り込みで do_IRQ から戻り、IRQ 出口パスに進みます。この IRQ がカーネル内から呼び出された場合、TIF_NEED_RESCHED が設定されているかどうかを確認し、設定されている場合はpreempt_schedule_irqを呼び出します。これにより、実行中の割り込みが一時的に有効になりますschedule()
  5. IRQ がユーザー空間から呼び出された場合、戻る前に何かする必要があるかどうかを最初に確認します。その場合、retint_carefulに移動し、保留中の再スケジュール (および必要に応じて直接呼び出し) と保留中のシグナルのチェックの両方をチェックし、重要なフラグが設定されなくなるまでschedule()、別のラウンドに戻ります。retint_check
  6. 最後に、GS を復元し、割り込みハンドラから戻ります。

に関してはswitch_to(); ( x86-32switch_to()で)行うことは次のとおりです。

  1. 後でこのタスクに戻るときのために、EIP (命令ポインター) と ESP (スタック ポインター) の現在の値を保存します。
  2. の値を切り替えますcurrent_task。この時点で、currentは新しいタスクを指しています。
  3. 新しいスタックに切り替えてから、切り替え先のタスクによって保存された EIP をスタックにプッシュします。後で、この EIP を返信アドレスとして使用して返信が実行されます。これは、以前に呼び出した古いコードに戻る方法ですswitch_to()
  4. __switch_to()を呼び出します。この時点で、currentは新しいタスクを指しており、新しいタスクのスタックにいますが、他のさまざまな CPU 状態は更新されていません。__switch_to()FPU、セグメント記述子、デバッグレジスタなどの状態の切り替えを処理します。
  5. から戻ると、手動でスタックにプッシュされ__switch_to()た戻りアドレスが に返され、新しいタスクのswitch_to()前の場所に実行が戻されます。switch_to()切り替え先のタスクで実行が完全に再開されました。

x86-64 は非常に似ていますが、ABI が異なるため、状態の保存/復元を少し多く行う必要があります。

于 2011-06-29T22:17:09.597 に答える