自作 「Linux カーネルはプリエンプティブである」とはどういう意味ですか?




カーネル の 意味 (7)

プリエンプティブなマルチタスキングのシンプルなビューを想像してみてください。 I / Oを使用せずにカーネルコールを実行するという、2つのユーザータスクがあります。 これらの2つのタスクは、マルチタスクオペレーティングシステムで実行できるように特別な操作を行う必要はありません。 通常、タイマ割り込みに基づくカーネルは、あるタスクが別のタスクを実行させるために一時停止する時間であると判断します。 問題のタスクは、何かが起こったことを完全に認識していません。

しかし、たいていのタスクでは、システムコール経由でカーネルを要求することがあります。 この場合、同じユーザーコンテキストが存在しますが、CPUはそのタスクに代わってカーネルコードを実行しています。

古いLinuxカーネルは、カーネルコードの実行中にタスクが優先されることはありません。 (I / O操作は常に自主的にスケジュールを変更することに注意してください。カーネルコードには、CPUを集中的に使用する操作(リストのソートなど)があります。

システムがカーネルコードを実行している間にそのタスクを先取りすることを許可している場合、「プリエンプティブカーネル」と呼ばれるものがあります。 このようなシステムは、システムコール中に発生する可能性のある予測できない遅延の影響を受けません。したがって、組み込みタスクまたはリアルタイムタスクに適している可能性があります。

たとえば、特定のCPU上で2つのタスクが利用可能で、1つがシステムコールを完了するために5msかかる場合、もう1つが2msごとにオーディオパイプを供給する必要のあるMP3プレーヤーアプリケーションである場合、音声が吃音する可能性があります。

プリエンプションに対する引数は、タスクコンテキストで呼び出される可能性のあるすべてのカーネルコードがプリエンプションで生き残ることができなければならないということです。たとえば、デバイスドライバのコードが貧弱です。たとえば、前もってオペレーションを完了できればよいかもしれません。そのプロセッサ上で他のタスクを実行することができます。 (最近の例外ではなくマルチプロセッサシステムの場合、すべてのカーネルコードは再入可能でなければならないので、今日の議論はそれほど重要ではありません。)さらに、悪質なシステムコールを改善することによって同じ目標を達成できれば潜在的には、プリエンプションは不要です。

妥協はCONFIG_PREEMPT_VOLUNTARYです。カーネル内部の特定の箇所でタスクスイッチを許可しますが、どこにでも置くことはできません。 カーネルコードが駄目になる可能性がある場所の数が少ない場合、これは複雑さを管理しながらレイテンシを減らす安価な方法です。

私は、Linuxカーネルは、ほとんどのUnixカーネルとは異なる先制型であると読んでいます。 だから、カーネルが先制的になることは本当に何を意味するのでしょうか?

いくつかの類推または例は、純粋な理論的説明よりも良いでしょう。


Linuxカーネルはプリエンプティブであることを意味します。カーネルはプリエンプションをサポートしています。

たとえば、読み込みシステムコールを実行しているカーネルモードで動作しているP1(高優先度)とP2(低優先度)の2つのプロセスがあります。 P2が実行中で、カーネルモードであり、P2の実行がスケジュールされているとします。

カーネルプリエンプションが利用可能な場合、プリエンプションはカーネルレベルで発生する可能性があります。つまり、P2はプリエンプトされてもスリープ状態になり、P1は引き続き実行されます。

カーネルプリエンプションが利用できない場合、P2はカーネルモードであるため、システムはP2が完了するまで単純に待機します


Linuxカーネルバージョン2.5.4より前では、Linuxカーネルはプリエンプティブではありませんでした。つまり、カーネルモードで実行されているプロセスは、プロセッサから離れるか、または一部の入力出力処理が完了するまで待機するまでプロセッサから移動できません。

一般に、ユーザモードのプロセスは、システムコールを使用してカーネルモードに入ることができます。 これまでは、カーネルが非プリエンプティブである場合、優先度の低いプロセスは、システムコールを繰り返し呼び出すことによってプロセッサへのアクセスを拒否し、カーネルモードを維持することによって、優先度の高いプロセスを優先して反転させる可能性がありました。 優先度の低いプロセスのタイムスライスが切れても、カーネルでの作業や自発的に放棄された制御が完了するまで実行を継続します。 優先度の高い処理が実行されるのを待っているときに、ユーザーが入力中のテキストエディタやオーディオバッファを補充する準備ができている場合は、対話型のパフォーマンスが低下します。 このように、非プリエンプティブカーネルは、当時の大きな欠点でした。


私はそれが2.6より先制化されたと思う。 プリエンプティブな手段は、新しいプロセスを実行する準備ができている場合、CPUは新しいプロセスに割り当てられ、実行中のプロセスは協調してCPUを放棄する必要はありません。


プリエンプションは、カーネルに並列性の印象を与えることを可能にします:あなたは1つのプロセッサしか持っていませんが(10年前に言えば)、すべてのプロセスが同時に実行されているように感じます。 これは、カーネルがあるプロセスから実行をプリエンプト(つまり、実行から取り除く)して、次のプロセスに(おそらく優先順位に従って)実行するためです。

EDITプリエンプティブカーネルは、プロセスが手を返すのを待たないので(つまり、システムコール中)、プロセスが大量のデータを計算し、どんな種類のyield関数も呼び出さなければ、他のプロセスは実行できません彼らの呼び出しを実行します。 このようなシステムは、実行時間の公平性を確保するためのプロセスの協力を求めているため、協力的であると言われています

EDIT 2プリエンプションの主な目的は、複数のタスク間でシステムの反応性を向上させることです。これはエンドユーザーには効果的ですが、一方ではサーバーは最高のスループットを達成したいので、必要ありません。 (Linuxカーネルの設定から)


伝統的なunixカーネルは、カーネルコードの実行中にスレッドによって保持された単一のロックを持っていました。 したがって、他のカーネルコードはそのスレッドを中断することはできません。

これにより、カーネルリソースを使用する1つのスレッドで他のスレッドは存在しないことを知っていたので、カーネルの設計が容易になりました。 したがって、異なるスレッドは、互いにうまく動作できません。

シングルプロセッサシステムでは、これは多すぎる問題を引き起こさない。

しかし、マルチプロセッサシステムでは、異なるプロセッサやコア上の複数のスレッドがすべてカーネルコードを同時に実行したいという状況が発生する可能性があります。 これは、ワークロードのタイプに応じて、多くのプロセッサを持つことができますが、それらのすべてがお互いに待っている時間のほとんどを過ごすことを意味します。

Linux 2.6では、カーネルリソースが個々のロックによって保護されたより小さな単位に分割され、対応するリソースが使用されている間だけロックが保持されるようにカーネルコードがレビューされました。 したがって、異なるプロセッサは、同じリソース(ハードウェアリソースなど)へのアクセスを希望する場合に、お互いに待つだけです。


これは、オペレーティングシステムスケジューラが実行中のプロセスの実行を一時停止して、いつでもCPUを別のプロセスに任せることを意味します。 これを行うための通常の方法は、CPUが実行するCPU時間の「量子」を待っている各プロセスに与えることです。 スケジューラが期限切れになった後、スケジューラは別のクォンタムを別のプロセスに与えるためにコントロールを取り戻します(実行中のプロセスはこれを回避できません)。

この方法は、プロセスが中断されずにCPUを必要な時間すべて保持し、他のアプリケーションを実行させるために明示的に何らかの「yield」関数を呼び出さなければならない協調マルチタスクと頻繁に比較されます。 当然ながら、システムの感覚が詰まるのを避けるために、正常に動作するアプリケーションはCPUを頻繁に使用します。 それでも、アプリケーションにバグがある場合(たとえば、イールドコールなしの無限ループ)、CPUが障害のあるプログラムによって完全に保持されるため、システム全体がハングします。

最近のほとんどすべてのデスクトップOSは、リソースの面でより高価であっても、一般的にはより安定している(システムが常に制御されているため、システム全体をハングアップさせることが難しい) 一方、リソースが逼迫し、アプリケーションが正常に動作することが期待される場合、協調マルチタスキングが使用されます。 Windows 3は協調マルチタスクOSでした。 より最近の例は、オープンソースのPMPファームウェアの代替品であるRockBoxです。





multitasking