Interrupt-Handler - Interrupt handler

In der Computersystemprogrammierung , ein Interrupt - Handler , der auch als bekannten Unterbrechungsbehandlungsroutine oder ISR , ist ein spezieller Code - Block mit einem bestimmten zugeordneten Interrupt - Zustand. Interrupt-Handler werden durch Hardware-Interrupts, Software-Interrupt-Anweisungen oder Software- Ausnahmen eingeleitet und werden zum Implementieren von Gerätetreibern oder Übergängen zwischen geschützten Betriebsmodi, wie beispielsweise Systemaufrufen, verwendet .

Die traditionelle Form des Interrupt-Handlers ist der Hardware-Interrupt-Handler. Hardware-Interrupts entstehen durch elektrische Bedingungen oder Low-Level-Protokolle, die in digitaler Logik implementiert sind automatisches Eintreten in einen anderen Ausführungskontext (Berechtigungsebene) für die Dauer der Ausführung des Interrupt-Handlers. Im Allgemeinen werden Hardware-Interrupts und ihre Handler verwendet, um Bedingungen mit hoher Priorität zu handhaben, die die Unterbrechung des aktuellen Codes erfordern, den der Prozessor ausführt.

Später hat es sich für eine Software als zweckmäßig herausgestellt, den gleichen Mechanismus mittels eines Software-Interrupts (einer Form eines synchronen Interrupts) auslösen zu können. Anstatt eine hartcodierte Interrupt-Dispatch-Tabelle auf Hardware-Ebene zu verwenden, werden Software-Interrupts oft auf Betriebssystemebene als eine Form einer Rückruffunktion implementiert .

Interrupt-Handler haben eine Vielzahl von Funktionen, die je nachdem, was den Interrupt ausgelöst hat, und der Geschwindigkeit, mit der der Interrupt-Handler seine Aufgabe erledigt, variieren. Zum Beispiel löst das Drücken einer Taste auf einer Computertastatur oder das Bewegen der Maus Interrupts aus, die Interrupt-Handler aufrufen, die die Taste oder die Position der Maus lesen und die zugehörigen Informationen in den Speicher des Computers kopieren.

Ein Interrupt-Handler ist ein Low-Level-Gegenstück zu Event-Handlern . Interrupt-Handler haben jedoch einen ungewöhnlichen Ausführungskontext, viele harte Einschränkungen in Zeit und Raum, und ihre intrinsische asynchrone Natur macht es notorisch schwierig, sie durch die Standardpraxis zu debuggen (reproduzierbare Testfälle gibt es im Allgemeinen nicht), sodass spezielle Fähigkeiten erforderlich sind wichtige Teilmenge der Systemprogrammierung – von Softwareingenieuren, die auf der Hardware-Interrupt-Schicht tätig sind.

Unterbrechungsflags

Im Gegensatz zu anderen Event-Handlern wird von Interrupt-Handlern erwartet, dass sie als Teil ihrer Kernfunktionalität Interrupt-Flags auf geeignete Werte setzen.

Selbst in einer CPU, die verschachtelte Interrupts unterstützt, wird oft ein Handler erreicht, bei dem alle Interrupts global durch eine CPU-Hardwareoperation maskiert sind. In dieser Architektur würde ein Interrupt-Handler normalerweise die kleinste erforderliche Menge an Kontext speichern und dann das globale Interrupt-Sperr-Flag bei der ersten Gelegenheit zurücksetzen, um Interrupts mit höherer Priorität zu ermöglichen, den aktuellen Handler zu unterbrechen. Es ist auch wichtig für den Interrupt-Handler, die aktuelle Interrupt-Quelle durch eine Methode zu unterdrücken (oft durch Umschalten eines Flag-Bits irgendeiner Art in einem peripheren Register), damit der aktuelle Interrupt beim Beenden des Handlers nicht sofort wiederholt wird, was zu einer Endlosschleife führt .

Einen Interrupt-Handler zu verlassen, während sich das Interrupt-System unter allen Eventualitäten im exakt richtigen Zustand befindet, kann manchmal eine mühsame und anspruchsvolle Aufgabe sein, und seine Fehlbehandlung ist die Quelle vieler schwerwiegender Fehler, die das System vollständig zum Stillstand bringen. Diese Fehler treten manchmal intermittierend auf, wobei der fehlerhafte Edge-Fall für Wochen oder Monate im Dauerbetrieb nicht auftritt. Die formale Validierung von Interrupt-Handlern ist enorm schwierig, während das Testen normalerweise nur die häufigsten Fehlermodi identifiziert, so dass subtile, zeitweilige Fehler in Interrupt-Handlern oft an Endkunden geliefert werden.

Ausführungskontext

In einem modernen Betriebssystem ist der Ausführungskontext eines Hardware-Interrupt-Handlers beim Eintritt subtil.

Aus Leistungsgründen wird der Handler typischerweise im Speicher- und Ausführungskontext des laufenden Prozesses initiiert, zu dem er keine spezielle Verbindung hat (der Interrupt usurpiert im Wesentlichen den laufenden Kontext – die Prozesszeitabrechnung wird oft Zeit für die Behandlung von Interrupts aufbringen) der unterbrochene Vorgang). Im Gegensatz zum unterbrochenen Prozess wird der Interrupt jedoch normalerweise durch einen hartcodierten CPU-Mechanismus auf eine Berechtigungsebene erhöht, die hoch genug ist, um direkt auf Hardwareressourcen zuzugreifen.

Überlegungen zum Stapelplatz

In einem Low-Level-Mikrocontroller fehlen dem Chip möglicherweise Schutzmodi und er hat keine Speicherverwaltungseinheit (MMU). Bei diesen Chips ist der Ausführungskontext eines Interrupt-Handlers im Wesentlichen der gleiche wie der des unterbrochenen Programms, das typischerweise auf einem kleinen Stapel fester Größe läuft (die Speicherressourcen waren traditionell am unteren Ende extrem knapp). Verschachtelte Interrupts werden oft bereitgestellt, was die Stapelnutzung verschlimmert. Eine Haupteinschränkung für den Interrupt-Handler bei diesem Programmiervorhaben besteht darin, den verfügbaren Stapel im schlimmsten Fall nicht zu überschreiten, was den Programmierer erfordert, global über den Stapelplatzbedarf jedes implementierten Interrupt-Handlers und jeder Anwendungsaufgabe nachzudenken.

Eine Überschreitung des zugewiesenen Stack-Speicherplatzes (ein sogenannter Stack-Overflow ) wird von Chips dieser Klasse in der Regel in der Hardware nicht erkannt. Wenn der Stapel in einen anderen beschreibbaren Speicherbereich überschritten wird, funktioniert der Handler normalerweise wie erwartet, aber die Anwendung schlägt später (manchmal viel später) aufgrund des Nebeneffekts der Speicherbeschädigung des Handlers fehl. Wenn der Stack in einen nicht beschreibbaren (oder geschützten) Speicherbereich überschritten wird, tritt der Fehler normalerweise innerhalb des Handlers selbst auf (im Allgemeinen der einfachere Fall, um später zu debuggen).

In dem beschreibbaren Fall kann man einen Sentinel - Stack - Guard-einen festen Wert direkt über das Ende des Rechtsstapel , dessen Wert implementieren kann überschrieben werden, aber nie sein wird , wenn das System korrekt arbeitet. Es ist üblich, regelmäßig eine Beschädigung des Stapelschutzes mit einer Art Watchdog-Mechanismus zu beobachten. Dadurch werden die meisten Stapelüberlaufbedingungen zu einem Zeitpunkt in der Nähe der störenden Operation abgefangen.

In einem Multitasking-System hat jeder Ausführungs-Thread typischerweise seinen eigenen Stapel. Wenn kein spezieller Systemstack für Interrupts bereitgestellt wird, verbrauchen Interrupts Stapelplatz von jedem unterbrochenen Ausführungsthread. Diese Designs enthalten normalerweise eine MMU, und die Benutzer-Stacks sind normalerweise so konfiguriert, dass ein Stapelüberlauf von der MMU abgefangen wird, entweder als Systemfehler (zum Debuggen) oder um Speicher neu zuzuordnen, um den verfügbaren Speicherplatz zu erweitern. Speicherressourcen auf dieser Ebene von Mikrocontrollern sind in der Regel weit weniger eingeschränkt, sodass Stacks mit einem großzügigen Sicherheitsspielraum zugewiesen werden können.

In Systemen, die hohe Thread-Zahlen unterstützen, ist es besser, wenn der Hardware-Interrupt-Mechanismus den Stack auf einen speziellen System-Stack umschaltet, so dass keiner der Thread-Stacks die Verwendung verschachtelter Interrupts im schlimmsten Fall berücksichtigen muss. Winzige CPUs wie das 8-Bit- Motorola 6809 von 1978 haben separate System- und Benutzer-Stack-Pointer bereitgestellt.

Einschränkungen in Bezug auf Zeit und Parallelität

Aus vielen Gründen ist es sehr erwünscht, dass der Interrupt-Handler so kurz wie möglich ausgeführt wird, und es wird dringend davon abgeraten (oder verboten), dass ein Hardware-Interrupt potenziell blockierende Systemaufrufe aufruft. In einem System mit mehreren Ausführungskernen sind auch Überlegungen zum Wiedereintritt von größter Bedeutung. Wenn das System Hardware- DMA vorsieht , können auch mit nur einem einzigen CPU-Kern Parallelitätsprobleme auftreten. (Es ist nicht ungewöhnlich, dass einem Mid-Tier-Mikrocontroller Schutzstufen und eine MMU fehlen, aber dennoch eine DMA-Engine mit vielen Kanälen bereitgestellt wird; in diesem Szenario werden viele Interrupts typischerweise von der DMA-Engine selbst ausgelöst , und der zugehörige Interrupt-Handler ist erwartet, vorsichtig vorzugehen.)

Es hat sich eine moderne Praxis entwickelt, Hardware-Interrupt-Handler in Front-Half- und Back-Half-Elemente zu unterteilen. Die vordere Hälfte (oder erste Ebene) empfängt den anfänglichen Interrupt im Kontext des laufenden Prozesses, erledigt die minimale Arbeit, um die Hardware in einen weniger dringenden Zustand wiederherzustellen (wie das Leeren eines vollen Empfangspuffers) und markiert dann die hintere Hälfte (oder zweite Ebene) zur Ausführung in naher Zukunft mit der entsprechenden Planungspriorität; einmal aufgerufen, arbeitet die hintere Hälfte in ihrem eigenen Prozesskontext mit weniger Einschränkungen und vervollständigt die logische Operation des Handlers (wie das Übermitteln der neu empfangenen Daten an eine Betriebssystem-Datenwarteschlange).

Geteilte Handler in modernen Betriebssystemen

In mehreren Betriebssystemen- Linux , Unix , macOS , Microsoft Windows , z / OS , DESQview und einige andere Betriebssysteme in den letzten-Interrupt - Handler verwendet werden , in zwei Teile geteilt: der ersten Ebene Interrupt - Handler ( FLIH ) und die Zweite -Level-Interrupt-Handler ( SLIH ). FLIHs werden auch als harte Interrupt-Handler oder schnelle Interrupt-Handler bezeichnet , und SLIHs werden auch als langsame/weiche Interrupt-Handler oder verzögerte Prozeduraufrufe in Windows bezeichnet.

Ein FLIH implementiert mindestens eine plattformspezifische Interrupt-Behandlung ähnlich wie Interrupt-Routinen . Als Reaktion auf einen Interrupt gibt es einen Kontextwechsel , und der Code für den Interrupt wird geladen und ausgeführt. Die Aufgabe eines FLIH ist , um schnell die Interrupt - Service oder zur Aufzeichnung plattformspezifische kritische Informationen, die zum Zeitpunkt der Unterbrechung nur verfügbar ist, und plant für die weiteren langlebige Interrupt - Behandlung der Ausführung eines SLIH.

FLIHs verursachen Jitter bei der Prozessausführung. FLIHs maskieren auch Interrupts. Die Reduzierung des Jitters ist für Echtzeit-Betriebssysteme am wichtigsten , da sie gewährleisten müssen, dass die Ausführung eines bestimmten Codes innerhalb einer vereinbarten Zeit abgeschlossen wird. Um Jitter zu reduzieren und das Potenzial für Datenverlust durch maskierte Interrupts zu reduzieren, versuchen Programmierer, die Ausführungszeit eines FLIH zu minimieren, indem sie so viel wie möglich zum SLIH verlagern. Mit der Geschwindigkeit moderner Computer können FLIHs alle geräte- und plattformabhängigen Handhabungen implementieren und einen SLIH für weitere plattformunabhängige langlebige Handhabung verwenden.

FLIHs, die Hardware bedienen, maskieren typischerweise ihren zugehörigen Interrupt (oder halten ihn je nach Fall maskiert), bis sie ihre Ausführung abgeschlossen haben. Ein (ungewöhnlicher) FLIH, der seinen zugeordneten Interrupt demaskiert, bevor er abgeschlossen ist, wird als Wiedereintritts-Interrupt-Handler bezeichnet . Wiedereintretende Interrupt-Handler können einen Stapelüberlauf von mehreren Preemptions durch denselben Interrupt-Vektor verursachen und werden daher normalerweise vermieden. In einem Prioritäts-Interrupt- System maskiert der FLIH auch (kurzzeitig) andere Interrupts gleicher oder geringerer Priorität.

Ein SLIH schließt lange Interrupt-Verarbeitungsaufgaben ähnlich wie ein Prozess ab. SLIHs haben entweder einen dedizierten Kernel- Thread für jeden Handler oder werden von einem Pool von Kernel-Worker-Threads ausgeführt. Diese Threads sitzen in einer Ausführungswarteschlange im Betriebssystem, bis ihnen Prozessorzeit zur Verfügung steht, um die Verarbeitung des Interrupts durchzuführen. SLIHs können eine langlebige Ausführungszeit haben und werden daher normalerweise ähnlich wie Threads und Prozesse geplant.

Unter Linux werden FLIHs als obere Hälfte und SLIHs als untere Hälfte oder untere Hälfte bezeichnet . Dies unterscheidet sich von der Benennung, die in anderen Unix-ähnlichen Systemen verwendet wird, wo beide ein Teil der unteren Hälfte sind .

Siehe auch

Verweise