GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Wie wird ein Interrupt in Linux behandelt?

Hier ist eine allgemeine Ansicht der Verarbeitung auf niedriger Ebene. Ich beschreibe eine einfache typische Architektur, reale Architekturen können komplexer sein oder sich in einer Weise unterscheiden, die auf dieser Detailebene keine Rolle spielt.

Wenn ein Interrupt auftritt, prüft der Prozessor, ob Interrupts maskiert sind. Wenn dies der Fall ist, passiert nichts, bis sie entlarvt werden. Wenn Interrupts demaskiert werden und es anstehende Interrupts gibt, wählt der Prozessor einen aus.

Dann führt der Prozessor den Interrupt aus, indem er zu einer bestimmten Adresse im Speicher verzweigt. Der Code an dieser Adresse wird als Interrupt-Handler bezeichnet. Wenn der Prozessor dorthin verzweigt, maskiert er Interrupts (so dass der Interrupt-Handler die ausschließliche Kontrolle hat) und speichert den Inhalt einiger Register an einer Stelle (normalerweise andere Register).

Der Interrupt-Handler tut, was er tun muss, typischerweise durch Kommunikation mit dem Peripheriegerät, das den Interrupt ausgelöst hat, um Daten zu senden oder zu empfangen. Wenn der Interrupt durch den Timer ausgelöst wurde, könnte der Handler den OS-Scheduler auslösen, um zu einem anderen Thread zu wechseln. Wenn der Handler die Ausführung beendet, führt er eine spezielle Return-from-Interrupt-Anweisung aus, die die gespeicherten Register wiederherstellt und Interrupts demaskiert.

Der Interrupt-Handler muss schnell ausgeführt werden, da er verhindert, dass andere Interrupts ausgeführt werden. Im Linux-Kernel ist die Interrupt-Verarbeitung in zwei Teile geteilt:

  • Die „obere Hälfte“ ist der Interrupt-Handler. Es erledigt das notwendige Minimum, kommuniziert normalerweise mit der Hardware und setzt irgendwo im Kernel-Speicher ein Flag.
  • Die „untere Hälfte“ erledigt alle anderen notwendigen Verarbeitungen, zum Beispiel das Kopieren von Daten in den Prozessspeicher, das Aktualisieren von Kernel-Datenstrukturen usw. Es kann seine Zeit in Anspruch nehmen und sogar das Warten auf einen anderen Teil des Systems blockieren, da es mit Interrupts läuft aktiviert.

Wie zu diesem Thema üblich, lesen Sie für weitere Informationen Linux Device Drivers; Kapitel 10 handelt von Interrupts.


Gilles hat bereits den allgemeinen Fall eines Interrupts beschrieben, das Folgende gilt speziell für Linux 2.6 auf einer Intel-Architektur (ein Teil davon basiert auch auf Intels Spezifikationen).

Ein Interrupt ist ein Ereignis, das die Reihenfolge der vom Prozessor ausgeführten Anweisungen ändert.
Es gibt zwei verschiedene Arten von Interrupts:

  • Synchroner Interrupt (Ausnahme) erzeugt von der CPU während der Verarbeitung von Anweisungen
  • Asynchroner Interrupt (Interrupt) von anderen Hardwaregeräten ausgestellt

Ausnahmen werden durch Programmierfehler verursacht (z.B. Teilungsfehler , Seitenfehler , Überlauf ), die vom Kernel verarbeitet werden müssen. Er sendet ein Signal an das Programm und versucht, den Fehler zu beheben.

Die folgenden zwei Ausnahmen werden klassifiziert:

  • Vom Prozessor erkannte Ausnahme von der CPU erzeugt, während ein anomaler Zustand erkannt wird; in drei Gruppen unterteilt:Fehler kann generell korrigiert werden, Fallen eine Hinrichtung melden, Abbrüche sind schwerwiegende Fehler.
  • Programmierte Ausnahme vom Programmierer angefordert, wie eine Falle behandelt.

Interrupts können von E/A-Geräten (Tastatur, Netzwerkadapter, ...), Intervalltimern und (bei Mehrprozessorsystemen) anderen CPUs ausgegeben werden. Wenn ein Interrupt auftritt, muss die CPU ihren aktuellen Befehl stoppen und den neu eingetroffenen Interrupt ausführen. Er muss den alten unterbrochenen Prozesszustand speichern, um ihn (wahrscheinlich) wieder aufzunehmen, nachdem der Interrupt behandelt wurde.

Der Umgang mit Interrupts ist eine heikle Aufgabe:

  • Interrupts können jederzeit auftreten, der Kernel versucht, sie so schnell wie möglich aus dem Weg zu räumen
  • Ein Interrupt kann durch einen anderen Interrupt unterbrochen werden
  • Es gibt Bereiche im Kernel, die überhaupt nicht unterbrochen werden dürfen

Es sind zwei verschiedene Interrupt-Levels definiert:

  • Maskierbare Interrupts ausgegeben von E/A-Geräten; kann in zwei Zuständen vorliegen, maskiert oder unmaskiert. Es werden nur unmaskierte Interrupts verarbeitet.
  • Nichtmaskierbare Interrupts; kritische Fehlfunktionen (z. B. Hardwarefehler); immer von der CPU verarbeitet.

Jedes Hardwaregerät hat seine eigene Interrupt Request (IRQ)-Leitung. Die IRQs sind beginnend mit 0 nummeriert. Alle IRQ-Leitungen sind mit einem programmierbaren Interrupt-Controller (PIC) verbunden. Der PIC hört auf IRQs und weist sie der CPU zu. Es ist auch möglich, eine bestimmte IRQ-Leitung zu deaktivieren.
Moderne Multiprocessing-Linux-Systeme enthalten im Allgemeinen das neuere Advanced PIC (APIC), das IRQ-Anforderungen gleichmäßig auf die CPUs verteilt.

Der Mittelschritt zwischen einem Interrupt oder einer Ausnahme und der Handhabung davon ist die Interrupt Descriptor Table (IDT). Diese Tabelle verknüpft jeden Interrupt- oder Ausnahmevektor (eine Zahl) mit einem bestimmten Handler (z. B. Divide error wird von der Funktion divide_error() behandelt ).

Durch die IDT weiß der Kernel genau, wie er mit dem aufgetretenen Interrupt oder der Ausnahme umgehen soll.

Also, was macht der Kernel, wenn ein Interrupt auftritt?

  • Die CPU prüft nach jeder Anweisung, ob ein IRQ vom (A)PIC kommt
  • Falls ja, konsultiert den IDT, um den empfangenen Vektor einer Funktion zuzuordnen
  • Überprüft, ob der Interrupt von einer autorisierten Quelle ausgegeben wurde
  • Speichert die Register des unterbrochenen Prozesses
  • Rufen Sie die entsprechende Funktion auf, um den Interrupt zu behandeln
  • Laden Sie die zuletzt gespeicherten Register des unterbrochenen Prozesses und versuchen Sie, ihn fortzusetzen

Zuallererst sind am Interrupt-Handling beteiligte Hardware-Peripheriegeräte, Interrupt-Controller, CPU, Betriebssystem-Kernel und Treiber. Periphere Hardwaregeräte sind für die Interrupt-Erzeugung verantwortlich. Sie aktivieren Interrupt-Anforderungsleitungen, wenn sie die Aufmerksamkeit des Betriebssystemkerns wünschen. Diese Signale werden vom Interrupt-Controller gemultiplext, der für die Sammlung der Interrupt-Signale verantwortlich ist. Es ist auch verantwortlich für die Bestimmung der Reihenfolge, in der Unterbrechungssignale an die CPU weitergeleitet werden. Der Interrupt-Controller ist in der Lage, bestimmte Interrupt-Request-Leitungen (IRQL) vorübergehend zu deaktivieren und wieder zu aktivieren (IRQL-Maskierung). Der Interrupt-Controller leitet gesammelte Interrupt-Anforderungen sequentiell an die CPU weiter. Die CPU prüft nach Abschluss der Ausführung jeder Anweisung, ob es irgendwelche wartenden Interrupt-Anforderungen von der Interrupt-Steuereinheit gibt. Wenn die CPU feststellt, dass eine wartende Anforderung vorliegt UND das Interrupt-Freigabe-Flag im internen CPU-Steuerregister gesetzt ist, beginnt die CPU mit der Interrupt-Verarbeitung. Wie Sie sehen können, ist der Linux-Kernel durch Manipulation des Interrupt-Flags in der CPU und Kommunikation mit dem Interrupt-Controller in der Lage, die Interrupt-Akzeptanz zu steuern. Zum Beispiel kann Linux die Annahme von Interrupts von dem bestimmten Gerät deaktivieren oder die Annahme von Interrupts überhaupt deaktivieren.

Was passiert, wenn der Prozessor eine Interrupt-Anforderung erhält? Erstens deaktiviert die CPU automatisch Interrupts, indem sie das Interrupt-Flag zurücksetzt. Sie werden wieder aktiviert, sobald die Unterbrechungsbehandlung abgeschlossen ist. Gleichzeitig macht die CPU einen minimalen Arbeitsaufwand, der zum Umschalten der CPU vom Benutzermodus in den Kernelmodus erforderlich ist, so dass sie die Ausführung des unterbrochenen Codes wieder aufnehmen kann. Die CPU konsultiert spezielle CPU-Steuerungsstrukturen, die vom Linux-Kernel gefüllt werden, um eine Codeadresse zu finden, an die die Steuerung übergeben wird. Diese Adresse ist die Adresse der ersten Anweisung des Interrupt-Handlers, der Teil des Linux-Kernels ist.

Als erster Schritt der Interrupt-Behandlung identifiziert der Kernel den Vektor des empfangenen Interrupts, um festzustellen, welche Art von Ereignis im System aufgetreten ist. Der Unterbrechungsvektor definiert, welche Maßnahmen Linux ergreifen wird, um damit umzugehen. Als zweiter Schritt speichert Linux den Rest der CPU-Register (die nicht automatisch von der CPU gespeichert wurden) und die möglicherweise vom unterbrochenen Programm verwendet werden können. Dies ist eine sehr wichtige Aktion, da Linux Interrupts transparent in Bezug auf das unterbrochene Programm behandeln kann. Als dritter Schritt erreicht Linux das Umschalten in den Kernelmodus, indem es die Kernelumgebung und den dafür erforderlichen CPU-Status einstellt. Und schließlich Vektor abhängiger Interrupt-Handler wird aufgerufen. (Sie können sich das BUILD_INTERRUPT3-Makro in arch\x86\kernel\entry_32.S ansehen, um die zusätzlichen Details für ein Beispiel mit Bezug zur x86-Architektur zu erhalten.) Im Fall von Peripheriegeräten ist dies eine do_IRQ()-Routine. (Schauen Sie in arch\x86\kernel\irq.c nach)

Der vektorabhängige Interrupt-Handler wird normalerweise von Aufrufen an irq_enter() und irq_exit() umschlossen. Der Codebereich, der in einem Paar dieser Funktionen eingeschlossen ist, ist in Bezug auf alle anderen derartigen Bereiche atomar und ist auch in Bezug auf Paare von cli/sti atomar. Irq_enter() und irq_exit() erfassen auch einige Statistiken im Zusammenhang mit der Interrupt-Behandlung. Schließlich schaut der Kernel in die Tabelle vector_irq, um die IRQ-Nummer zu finden, die dem Vektor des empfangenen Interrupts zugewiesen ist, und ruft handle_irq() auf (von arch\x86\kernel \irq_32.c).

An diesem Punkt endet der gemeinsame Teil der Interrupt-Behandlung in Linux, da der Kernel die vom Gerätetreiber installierte geräteabhängige Interrupt-Handler-Routine als Teil des IRQ-Deskriptors betrachtet und aufruft. Wenn ein solcher Handler nicht vom Treiber installiert wurde, bestätigt der Kernel einfach den Interrupt auf dem Interrupt-Controller und verlässt den allgemeinen Interrupt-Handler.

Nach dem Ende der Interruptbehandlung stellt der Kernel den Zustand des zuvor unterbrochenen Programms wieder her und setzt diese Programmausführung fort.


Linux
  1. So verwenden Sie BusyBox unter Linux

  2. Wie ich Cron unter Linux verwende

  3. So installieren Sie NodeJS unter Linux

  4. Einführung in Linux-Interrupts und CPU-SMP-Affinität

  5. Wie werden \n und \r unter Linux und Windows unterschiedlich gehandhabt?

So installieren Sie Kali Linux

So installieren Sie FFmpeg unter Linux

So beenden Sie einen Prozess in Linux

So erstellen Sie eine Datei unter Linux

So benennen Sie Verzeichnisse in Linux um

So installieren Sie FreeOffice unter Linux