Windows system >> Windowsの知識 >  >> Linuxシステムチュートリアル >> Linuxチュートリアル >> Linuxカーネルのプリエンプションの実装メカニズムの分析

Linuxカーネルのプリエンプションの実装メカニズムの分析

  

この一連のブログ記事では、主にLinuxカーネルのいくつかの重要な概念と技術的原則を紹介します。その中の一部はオンライン概要によるもの、独自の「Linuxカーネルの設計と実装」およびまとめその目的は、Linuxカーネルに慣れていない人にも、Linuxカーネルの実装方法についての一般的な知識を持たせることです。
1.1カーネルプリエンプション

2.6新しいプリエンプティブカーネルとは、カーネルプリエンプション、つまりプロセスがカーネル空間にあるとき、優先度の高いタスクが発生したとき、現在のカーネルがプリエンプションを許可しているときに、現在のタスクを中断して、優先順位の高いプロセスを実行できます。

バージョン2.5.4より前では、Linuxカーネルはプリエンプティブではありませんでした高優先度プロセスは、カーネル内で実行されている低優先度プロセスを中断できず、CPUをプリエンプトできませんでした。プロセスがコア状態(ユーザープロセスがシステムコールを実行するなど)になると、プロセスが自発的にCPUを放棄しない限り、プロセスはカーネルが完了するか終了するまで実行を続けます。対照的に、プリエンプティブなLinuxカーネルでは、Linuxカーネルをユーザースペースのようにプリエンプトすることができます。現在のプロセスがユーザーモードであるかコア状態であるかにかかわらず、優先順位の高いプロセスが到着したときに、現在優先使用が許可されている場合、カーネルを優先使用できるLinuxは優先順位の高いプロセスの実行をスケジュールします。
1.2ユーザープリエンプション

カーネルがユーザー空間に戻ろうとしているときにneed reschedフラグが設定されていると、schedule()が呼び出され、ユーザープリエンプションが発生します。カーネルがユーザ空間を返すとき、それは安全であることを知っています。したがって、カーネルは、割り込みハンドラから戻ったかシステムコールの後に戻ったかにかかわらず、再調査が必要なフラグをチェックします。設定されていると、カーネルは別の(より適切な)プロセスを実行に移すでしょう。

手短に言うと、次の場合にユーザープリエンプションが発生します。
lシステムからユーザースペースを返します。
l割り込みハンドラからユーザ空間を返します。
1.3予測不可能なカーネルの特徴

カーネルの横取りをサポートしていないカーネルでは、カーネルコードは完了するまで実行される可能性があります。つまり、カーネルレベルのタスクが実行されているときにスケジューラにスケジュールを変更する方法はありません;カーネル内のタスクは調整されており、プリエンプティブではありません。もちろん、カーネルモードで実行されているプロセスは、CPUを積極的に放棄することができます(たとえば、システムコールサービスルーチンでは、カーネルコードはリソースを待機するためCPUを放棄します)。カーネルコードは、完了するまで(ユーザースペースに戻るまで)または明らかにブロックされるまで実行する必要があります。

シングルCPUの場合、このような設定はカーネルの同期と保護のメカニズムを非常に単純化します。これは、2つのステップで分析できます。

まず、カーネル内のプロセスに関係なく、自発的にCPUを放棄します(つまり、カーネル内でプロセスの切り替えは行われません)。プロセスがカーネルに入ると、それが完了するかカーネルから出るまで実行を続けます。カーネル内でプロセスが実行されるのはシリアルであるため、カーネル内で複数のプロセスを同時に実行することは不可能であるため、カーネルコードの設計は考慮されません。複数プロセスの同時実行によって引き起こされる並行性の問題Linuxカーネル開発者は、重要なリソースへの相互排他的アクセスを同時に実行する複雑なプロセスについて心配する必要はありません。プロセスがカーネルのデータ構造にアクセスして変更するとき、複数のプロセスが同時にクリティカルセクションに入るのを防ぐためにロックする必要はありません。割り込み処理ルーチンがアクセスしているデータ構造にアクセスすることも可能であれば、プロセスはクリティカルセクションに入る前に割り込み操作を閉じ、クリティカルセクションを出るときに割り込み操作を開くだけでよいのです。はい。

もう一度、CPUを自主的に放棄するプロセスを考えます。 CPUの放棄は任意でアクティブなので、カーネル内のプロセスの切り替えは事前に認識されており、それを知らずにプロセスの切り替えが行われることはありません。このようにして、カーネル全体でのプロセスの同時実行を考慮せずに、プロセス切り替えのプロセスで複数のプロセスが同時に実行されるときに発生する可能性がある並行性の問題だけを考慮する必要があります。
1.4なぜカーネルプリエンプションが必要なのですか?

Linuxではカーネルプリエンプションを実装することが重要です。まず、これはLinuxをリアルタイムシステムに適用するために必要です。リアルタイムシステムでは、応答時間に厳密な制限があり、リアルタイムデバイスのハードウェア割り込みによってリアルタイムプロセスが起動される場合は、限られた時間内に実行されるようにスケジュールする必要があります。 Linuxカーネルはプリエンプティブではなく、カーネル内でのシステムの滞留時間を決定できないため、Linuxはこの要件を満たしていません。実際、カーネルが長いシステムコールを実行すると、リアルタイムプロセスは、カーネル内で実行中のプロセスがカーネルを終了してスケジュールされるまで待機し、その結果生じる応答遅延は、今日のハードウェアでは最大100msです。

高いリアルタイムレスポンスが要求されるシステムには適していません。プリエンプティブカーネルは、Linuxのリアルタイムアプリケーションにとって重要なだけでなく、ビデオ、オーディオなどの低遅延アプリケーションに対するLinuxのサポートの欠如にも対処します。

Linux 2.5.4バージョンがリリースされたとき、カーネルを横取りすることの重要性のために、それはカーネルに横取りされることができ、そしてSMPのようなカーネルの標準のオプションの設定として。
1.5カーネルプリエンプションを許可しない条件

Linuxカーネルをプリエンプションにすべきでない場合がいくつかあります。これらのケースは次のとおりです。

(1)カーネルは割り込み処理を実行しています。 Linuxカーネルでは、プロセスは割り込みを横取りすることはできず(割り込みは打ち切り、他の割り込みを横取り、プロセスを打ち切り、割り込みを横取りすることはできません)、割り込みルーチンではプロセスのスケジューリングはできません。プロセススケジューリング関数schedule()がこれを判断し、割り込みで呼ばれた場合はエラーメッセージを表示します。

(2)カーネルは割り込みコンテキストの下半分(割り込みの下半分)を処理しています。ソフト割り込みは、ハードウェア割り込みが戻る前に実行され、まだ割り込みコンテキスト内にあります。

(3)カーネルのコードセグメントは、スピンロックスピンロック、writelock /readlock読み書きロックなどのロックを保護状態で保持しています。カーネル内のこれらのロックは、SMPシステムにおいて短時間で異なるCPU上で実行されているプロセスの同時実行の正しい実行を確実にするように設計されています。これらのロックを保持するとき、カーネルは横取りされるべきではありません、さもなければ横取りは他のCPUが長時間ロックを失い、死ぬ原因となります。

(4)カーネルがスケジューラSchedulerを実行しています。プリエンプションの理由は、新しいスケジュールを作成することです。スケジューラをプリエンプトしてスケジューラを実行する理由はありません。

(5)カーネルは各CPUの「プライベートな」データ構造操作(CPUごとの日付構造)に取り組んでいます。 SMPでは、CPUごとのデータ構造は暗黙的に保護されているため、CPUごとのデータ構造は保護されません(異なるCPUは異なるCPUごとのデータを持ち、他のCPUで実行されるプロセスは別のCPUを使用しません)。 CPUごとのCPUデータ)。ただし、横取りが許可されているが、横取りされた後にプロセスが再スケジュールされている場合は、他のCPUにスケジュールすることができます。この時点で、定義されたPer-CPU変数に問題があるため、横取りは禁止されます。

上記の条件下でLinuxカーネルがプリエンプトされないようにするために、プリエンプティブカーネルはカーネルプリエンプションロックと呼ばれる変数preempt_ countを使用します。この変数はプロセスのPCB構造task_structに設定されます。カーネルが上記の状態に入ることを望むときはいつでも、変数preempt_countは1だけインクリメントされ、カーネルがプリエンプションを許可しないことを示します。カーネルが上記の状態から出るたびに、変数preempt_countが1つデクリメントされ、プリエンプティブ判定とスケジューリングが同時に実行されます。

割り込みからカーネル空間を返すとき、カーネルはneed_reschedとpreempt_countの値をチェックします。 need_reschedが設定されていてプリエンプトカウントが0の場合、これは実行する必要があり安全にプリエンプトできる重要なタスクがある可能性があることを示し、その時点でスケジューラが呼び出されます。 preempt-countが0でなければ、カーネルはプリエンプション不可能な状態にあり、再スケジュールすることはできません。この時点で、現在の実行プロセスは通常通り割り込みから直接返されます。現在のプロセスによって保持されているすべてのロックが解放されると、preempt_countはゼロにリセットされます。この時点で、ロックを解放するコードはneed_reschedが設定されているかどうかを確認します。もしそうであれば、スケジューラが呼び出される。
1.6カーネルプリエンプションの機会

2.6カーネルでは、カーネルにプリエンプション機能が導入されましたが、再スケジュールが安全である限り、カーネルは実行中のタスクをいつでも捕捉できます。

では、再スケジュールはいつ安全ですか。 premptcountが0である限り、カーネルは横取りすることができます。通常、ロックと割り込みは、横取りされていない領域の兆候です。カーネルはSMPをサポートしているので、ロックが保持されていない場合、実行されているコードはリダイレクト可能です。つまり、差し替えられます。

カーネル内のプロセスがブロックされている場合、または明示的にschedule()を呼び出している場合は、カーネルの横取りも明示的に行われます。カーネルを安全に横取りできるようにするための追加のロジックは必要ないため、この形式のカーネル横取りは常にサポートされています(実際にはCPUを放棄するイニシアチブを取っています)。コードが明示的にschedule()を呼び出す場合は、安全に横取りされる可能性があることは明らかです。

コアプリエンプションが発生する可能性があります。
l割り込みハンドラが実行されてカーネル空間に戻る前。
lソフト割り込みのロック解除や有効化など、カーネルコードが再びプリエンプティブになっている場合(local_bh_enable)。
lカーネル内のタスクが明示的にschedule()を呼び出した場合l lカーネル内のタスクがブロックされている場合(これもschedule()が呼び出される原因となります。)

Copyright © Windowsの知識 All Rights Reserved