4. Interrupt-Handler
Der Interrupt-Handler in Linux ist sehr einzigartig. Sein Interrupt-Handler ist in zwei Teile unterteilt: obere Hälfte und untere Hälfte. Der Grund, warum es eine obere und eine untere Hälfte gibt, liegt ausschließlich in der Effizienz der Interrupt-Verarbeitung.
Die Funktion der oberen Hälfte ist „Registrierungsunterbrechung“. Wenn ein Interrupt auftritt, hängt es die untere Hälfte der Interrupt-Routine im Gerätetreiber in die untere Hälfte der Ausführungswarteschlange des Geräts und wartet dann auf das Eintreffen eines neuen Interrupts. Auf diese Weise wird die obere Hälfte schnell ausgeführt und er kann mehr Unterbrechungen akzeptieren, die durch die Ausrüstung verursacht werden, für die sie verantwortlich ist. Der Grund, warum der obere Teil schneller ist, liegt darin, dass er Interrupts vollständig maskiert. Wenn die Ausführung nicht abgeschlossen ist, können andere Interrupts nicht rechtzeitig verarbeitet werden und können nur warten, bis der Interrupt-Handler ausgeführt wird. Um möglichst viele vom Gerät generierte Interrupts bedienen und verarbeiten zu können, muss der Interrupt-Handler daher schnell sein.
Allerdings ist die Verarbeitung einiger Interrupt-Ereignisse komplizierter, sodass der Interrupt-Handler etwas mehr Zeit aufwenden muss, um die Sache abzuschließen. Aber wie kann der Widerspruch gelöst werden, der darin besteht, komplexe Verarbeitungen in kurzer Zeit abzuschließen? Zu diesem Zeitpunkt führte Linux das Konzept der unteren Hälfte ein. Der größte Unterschied zwischen der unteren und der oberen Hälfte besteht darin, dass die untere Hälfte unterbrechbar ist, während die obere Hälfte nicht unterbrechbar ist.
Die untere Hälfte macht fast alles, was der Interrupt-Handler tut, weil die obere Hälfte die untere Hälfte nur in die Interrupt-Verarbeitungswarteschlange des Geräts einreiht, für das sie verantwortlich ist, und sich dann um nichts kümmert. Die untere Hälfte ist im Allgemeinen dafür verantwortlich, das Gerät zu beobachten, um Ereignisinformationen zu erhalten, die Interrupts generieren, und auf der Grundlage dieser Informationen (normalerweise durch Lesen der Register auf dem Gerät erhalten) eine entsprechende Verarbeitung durchzuführen. Wenn es Zeiten gibt, in denen die andere Hälfte nicht weiß, was sie tun soll, nutzt sie den berühmten Straußalgorithmus, um das Problem zu lösen – um es ganz klar auszudrücken: Sie ignoriert das Ereignis.
Da die untere Hälfte unterbrechbar ist, kann die untere Hälfte vorübergehend unterbrochen werden, wenn andere Geräte während des Betriebs unterbrochen werden, bis die obere Hälfte dieses Geräts nicht mehr läuft. Eines muss jedoch beachtet werden: Wenn ein Geräte-Interrupt-Handler ausgeführt wird, unabhängig davon, ob er in der oberen oder unteren Hälfte ausgeführt wird, werden vom Gerät neue Interrupts generiert, solange der Interrupt-Handler nicht verarbeitet wurde In diesem Zeitraum werden Interrupts ignoriert. Da Interrupt-Handler nicht reentrant sind, kann derselbe Interrupt-Handler nicht parallel ausgeführt werden.
Vor Linux Kernel 2.0 wurden Interrupts in schnelle Interrupts und langsame Interrupts unterteilt (wir werden hier nicht über Pseudo-Interrupts sprechen). Die untere Hälfte des schnellen Interrupts ist ebenfalls unterbrechungsfrei, was sicherstellt, dass er schneller ausgeführt wird. Da die Hardwareebene jedoch weiter steigt, gibt es keinen Unterschied in der Laufgeschwindigkeit von schnellen Interrupts und langsamen Interrupts. Um die Effizienz der Interrupt-Routine-Transaktionsverarbeitung zu verbessern, sind ab Linux-Kernel 2.0 alle Interrupt-Handler vorhanden die Form langsamer Interrupts – Ihre untere Hälfte kann unterbrochen werden.
In der zweiten Hälfte können Sie jedoch auch Interrupts maskieren – wenn ein bestimmter Codeabschnitt nicht unterbrochen werden kann. Sie können cti, sti oder save_flag, restart_flag verwenden, um Ihre Idee zu verwirklichen. Informationen zu deren Verwendung und Unterschieden finden Sie im Abschnitt zur Interrupt-Verarbeitung des entsprechenden Nachschlagewerks in diesem Artikel.
Weitere Informationen finden Sie im in diesem Artikel angegebenen Nachschlagewerk. Ich werde hier nicht auf Details eingehen.
5. Setzen Sie das Interrupt-Flag-Bit
Bei der Verarbeitung eines Interrupts blockiert der Interrupt-Controller das Gerät, das den Interrupt ursprünglich gesendet hat, bis der letzte von ihm gesendete Interrupt verarbeitet wurde. Wenn das Gerät, das den Interrupt gesendet hat, während der Interrupt-Verarbeitung einen weiteren Interrupt sendet, geht der Interrupt daher für immer verloren.
Der Grund dafür ist, dass der Interrupt-Controller keine Interrupt-Informationen puffern kann. Wenn also ein neuer Interrupt eintrifft, bevor der aktuelle Interrupt verarbeitet wird, geht der neue Interrupt definitiv verloren. Dieser Mangel kann jedoch durch Setzen des „Set Interrupt Flag“ (sti) am Hauptprozessor (CPU) behoben werden, da der Hauptprozessor die Funktion hat, Interrupts zu puffern. Wenn „Interrupt-Flag-Bit setzen“ verwendet wird, kann die STI-Funktion verwendet werden, um den zuvor maskierten Interrupt zu bedienen, nachdem der Interrupt verarbeitet wurde.
Das Obige ist die ausführliche Analyse von Linux-Gerätetreiber-Interrupts (1) (2). Weitere verwandte Inhalte finden Sie auf der chinesischen PHP-Website (www.php.cn)!