6. 割り込みハンドラーの非再入性
前のセクションでは、割り込みをマスクする必要がある場合があると述べましたが、なぜこの割り込みをマスクする必要があるのでしょうか?これは、同じ割り込みルーチンを並列化することが技術的に不可能だからではなく、管理上の考慮事項によるものです。割り込み処理中に同じ IRQ からの新しい割り込みをブロックする必要があるのは、割り込みハンドラがリエントラントではないため、同じ割り込みハンドラを並列実行できないためです。ここで例を示します。この例から、割り込みハンドラーを並列化できる場合、ドライバーのロックが発生する可能性が高いことがわかります。ドライバーがロックされても、オペレーティング システムが必ずしもクラッシュするわけではありませんが、ロックされたドライバーによってサポートされているデバイスは使用できなくなります。デバイス ドライバーが停止すると、デバイスも停止します。
PS1 をトリガーするイベントにより、A1 が割り込みを生成し、次に B1 が R1 内の既存のデータを読み取り、コード C1 がデータを R2 に書き込みます。 PS2 をトリガーするイベントにより、A2 が割り込みを生成し、次に B2 が R1 のデータを削除し、次に C2 が R2 のデータを読み取ります。
PS1が先に生成され、A1とB1の間で実行される場合、PS2が生成されると、A2は割り込みを生成し、PS2を割り込み(タスクキューの最後までハングアップ)、その後R1コンテンツを削除します。 PS2 が C2 に実行されるとき、C1 はまだ R2 にデータを書き込んでいないため、C2 はここで一時停止され、PS2 はデータが読み取れるときに信号によって目覚めるまでコード C2 でスリープします。これは、PS1 の B2 が元々読みたかった R1 のデータが PS2 の B2 によって削除されたためです。そのため、PS1 ページは、読み込むデータがあるときに信号によって目覚めるまで B1 でスリープ状態になります。このようにすると、PS1 と PS2 が目覚めるイベントは発生しないため、PS1 と PS2 はロックされます。
デバイス ドライバーはデバイス レジスタを処理する必要があるため、デバイス レジスタはグローバル変数であるため、リエントラントなコードを記述するのは困難です。したがって、最も簡単な方法は、同じデバイスの割り込みハンドラの並列処理を禁止することです。つまり、デバイスの割り込みハンドラはリエントラントではありません。
1 つ明確にしておきたいのは、バージョン 2.0 以降の Linux カーネルでは、すべての上位半分は中断不可能です (上位半分の操作はアトミックです)。異なるデバイスの下位半分は相互に中断できますが、特定の下位半分は中断できません。それ自体が中断されます (つまり、同じ下位半分を並列化することはできません)。
割り込みハンドラーは非再入可能である必要があるため、プログラマは再入可能コードの作成について心配する必要はありません。私の経験では、再入可能なデバイス ドライバーを作成することは可能ですが、再入可能な割り込みハンドラーを作成することは非常にまれで、ほぼ不可能です。
7. 競合状態の発生を回避する
競合状態が発生すると、デッドロックが発生し、深刻な場合にはシステム全体がロックされる可能性があることは誰もが知っています。したがって、競合状態を必ず回避してください。ここでは多くは言いませんが、皆さんは 1 つのことに注意する必要があります。それは、割り込みによって引き起こされる競合状態のほとんどは、割り込みがスリープ状態になっているカーネル プロセスによって引き起こされるということです。したがって、割り込みを実装するときは、必要に応じて cli、sti または save_flag、restore_flag を使用してプロセスをスリープ状態にするように注意する必要があります。具体的な内容については、本文中に記載されている参考書籍をご参照ください。
8. 実装
ドライバーの割り込みルーチンをどのように実装するかは、読者の問題です。短いルーチンのソース コードを注意深く読み、ドライバー割り込みルーチンの作成ルールを理解していれば、独自の割り込みルーチンを作成できます。概念が正しく、コードが正しいルールに基づいて記述されている限り、それは意味があります。私は常に概念が第一であり、正しく考えるためには正しい概念が必要であることを強調しています。
(T114)
上記は、Linux デバイス ドライバーの割り込み (1) (3) の詳細な分析です。さらに関連するコンテンツについては、PHP 中国語 Web サイト (www.php.cn) に注目してください。