1. カーネルタイマー 1. 基本概念
特定のシナリオでは、特定の時間後に特定のアクションを実行する必要があり、待機することで CPU を無駄にしたくない場合、タイマーは非常に適したメカニズムです。タイマーは、将来の特定の時点で関数を実行し、特定のタスクを完了するために使用されます。
カーネルタイマーは、指定された時点で特定のパラメーターを使用して特定の関数を呼び出すようにカーネルに指示します。タイマーは登録者上で非同期に実行されます。タイマーの実行中、タイマーを登録したタスクはスリープしているか、他のプロセッサ上で実行されているか、かなり前に終了している可能性があります。
Linux のカーネル タイマーは、(ソフト) 割り込み Linux アプリケーション タイマー に基づいて実装されます。つまり、プロセス コンテキストではなく割り込みコンテキスト内にあります。非プロセスコンテキストでは従うべき原則がいくつかあります:
ユーザースペースへのアクセスは許可されていません
電流は無意味なので利用できません
眠れない、スケジュールを立てることができない。スケジュールやある種の wait_event を呼び出すことはできません。また、スリープを引き起こす可能性のある関数を呼び出すこともできません。セマフォは休止状態を引き起こす可能性があるため、セマフォも使用できません。
カーネル コードは、関数 in_interrupt() を呼び出すことで、現在割り込みコンテキスト内にあるかどうかを判断できます。それが 0 以外を返す限り、それは割り込みコンテキスト内にあることを意味します。カーネルは、in_atomic() を呼び出すことによって、スケジューリングが現在許可されているかどうかを判断できます。スケジューリングが許可されない状況には、割り込みコンテキスト内およびキャリア ロックを所有するコンテキスト内にあることが含まれます。
タイマーは非同期で実行されるため、タイマー処理関数は相互排他保護に注意する必要があります。
2.Linuxカーネルでサポートされるタイマー
Linux カーネルは 2 種類のタイマーをサポートしています:
クラシックタイマー: 精度がコンピュータークロックの割り込み頻度に依存するタイマー。通常、タイマーの精度は比較的低く、1000/HZms の精度です。このタイマーは固定周波数、つまり 1000/HZms ごとに生成されます。ダイナミック クロック機能が有効になっていない場合、タイマーの期限切れ時に実際のタイミングの乱れは発生しない可能性があります。たとえば、11 ミリ秒、52 ミリ秒、および 78 ミリ秒の期限切れタイマーのみがシステムに追加され、これらのタイマーは正確に期限切れになります。タイマーは 4 ms の倍数 (4、8、12...) で期限切れになるため、タイマーが期限切れになる時点でタイミングの乱れが必ずしも発生するとは限りません。
高フレームレートタイマー: クラシックタイマーの精度は比較的低く、場合によっては、中国の Linux オペレーティングシステムのマルチメディアアプリケーションなど、より高精度のタイマーが必要となるため、このタイプのタイマーがシステムに導入されています。このタイマーは基本的にいつでも発生する可能性があります。
ここでは 2 つの例外の概念も必要です:
動的クロック: 周期クロック Linux アプリケーション タイマー は、タスクを実際に実行する必要がある場合にのみアクティブ化され、それ以外の場合、周期クロック テクノロジは無効になります。このアプローチでは、アイドル実行をスケジュールする必要がある場合は周期クロックを無効にしてから、次のタイマーが期限切れになるか割り込みが発生するまで周期クロックを有効にします。ワンショット クロックは、動的クロックを実現するための前提条件です。動的クロックの主な特徴は、必要に応じてクロックを停止または再起動できることであり、純粋な周期クロックはこれらのシナリオには適していないためです。
周期時計: 時計の時刻を周期的に形成する時計。
アプリケーションの観点から見ると、タイマーには主に 2 つの用途があります:
タイムアウト: 一定時間後に発生する妨害を示します。実際、タイムアウトを使用する場合、ほとんどの場合、タイムアウトの発生が予想されず、タイムアウト前にタイマーがキャンセルされることがよくあります。さらに、タイムアウト インシデントはキャンセルされませんが、多くの場合、正確なインシデントではありません。たとえば、ネットワークで使用されるさまざまなタイムアウト タイマーは、この時点より前にタイムアウトが存在しない場合に、キャンセルされる可能性があることを意味します。 ... と考えられますが、この時間の値は多くの場合、経験値または計算値であり、正確な時間要件ではありません。このような状況では、従来のタイマーで十分です。
タイマー: たとえば、サウンドを再生するときに、データを定期的にサウンド カードに送信する必要があるため、特定の時点でデータがサウンド カードに送信されない場合には、厳しい要件が課されます。やがて音の歪みが生じます。このとき、高精度のタイマーを使用する必要があります。
Linux は、設定を通じて次のモードで動作するように指示できます:
高フレームレートのダイナミッククロック
高フレームレート周期クロック
低ビットレートのダイナミッククロック
低コードレート周期クロック
3. 低ビットレートのカーネルタイマー
低ビットレート タイマーは最も一般的なカーネル タイマーであり、カーネルはプロセッサのクロック割り込みまたはその他の適切な周期クロック ソースをタイマーの時間ベースラインとして使用します。クロック割り込みは、1 秒あたり HZ 回で定期的に発生します。この割り込みに対応するタイマー処理関数は通常 timer_interrupt ですが、この関数の処理では最終的に do_timer と update_process_timers に調整されます。このうち、do_timer はシステム全体およびグローバルなタスク (jiffies の更新とプロセス統計の処理) を担当し、後者の関数はプロセス統計を実行して TIMER_SOFTIRQ を形成し、スケジューラに時間認識を提供します。
タイマーが期限切れになると、タイマー処理関数が呼び出される前にタイマーがアクティベーション配列から削除されるため、この実行後一定時間後に再実行したい場合は、再度タイマーを追加する必要があります。デバイス。 SMP システムでは、タイマー機能は登録された CPU によって実行されます。
カーネルタイマーの実装は、次の要件と前提条件を満たす必要があります:
タイマー管理は可能な限りシンプルでなければなりません。
アクティビティタイマーが大幅に短縮された場合、設計には優れたスケーラビリティが必要です
ほとんどのタイマーは数秒以内、長くても数分以内に期限切れになりますが、長時間遅延するタイマーは非常にまれです。
タイマーは、登録された場所と同じ CPU 上で実行される必要があります。
低ビットレートのカーネルタイマーの実装は非常に賢明です。これは、CPU ごとのデータ構造に基づいています。 timer_list の基本配列には、この構造体を指すポインターが含まれています。 Base が NULL の場合、このタイマーは実行するために呼び出されていません。それ以外の場合、このポインターはどのデータ構造 (つまり、どの CPU) がそれを実行しているかを示します。
カーネル コードが (add_timer または mod_timer 経由で) タイマーを登録するたびに、操作は最終的に inner_add_timer (kernel/timer.c 内) によって実行され、タイマー内の現在の CPU に関連付けられた「カスケード テーブル」に新しいタイマーが追加されます。一方向配列。
カスケードテーブルの仕組み:
次の 0 ~ 255 ジフィー以内にタイマーの期限が切れた場合、期限切れを使用して、短期タイマー用に特別に提供されている 256 個の配列の 1 つに追加されます (つまり、どの配列に追加されるかは有効期限ビットによって決まります)。決定された下位 8 ビット) どの配列に追加するかを決定します
さらに将来 (ただし 16384 jiffies より前) に期限切れになる場合は、64 個の配列の 1 つに追加されます。これらの 64 個の配列は、期限切れの 6 ビットに関連付けられ、どの配列が使用されるかを決定します。 。
同様の手法が有効期限ビット 14 ~ 19、20 ~ 25、および 26 ~ 31 にも適用されます。
後日タイマーが切れた場合
以上がLinux カーネル タイマーの詳細: 割り込みベースの非同期メカニズムと非プロセス コンテキストの原則の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。