EINTR: Gibt es eine Begründung dafür?

10

Smalltalk als Hintergrund

EINTRist der Fehler, den sogenannte unterbrechbare Systemaufrufe zurückgeben können. Wenn ein Signal auftritt, während ein Systemaufruf ausgeführt wird, dieses Signal nicht ignoriert wird und ein Signalhandler dafür definiert wurde, der SA_RESTARTnicht gesetzt ist, und dieser Handler dieses Signal verarbeitet, gibt der Systemaufruf den EINTRFehlercode zurück.

Als Randnotiz habe ich diesen Fehler sehr oft ncursesin Python erhalten.

Die Frage

Gibt es eine Begründung für dieses im POSIX-Standard festgelegte Verhalten? Man kann verstehen, dass es möglicherweise nicht möglich ist, fortzufahren (abhängig vom Kernel-Design). Was ist jedoch der Grund dafür, dass es nicht automatisch auf Kernel-Ebene neu gestartet wird? Ist dies aus alten oder technischen Gründen? Wenn dies aus technischen Gründen geschieht, sind diese Gründe heute noch gültig? Wenn dies aus alten Gründen geschieht, wie ist dann die Geschichte?

Hibou57
quelle

Antworten:

13

Es ist schwierig, in einem Signalhandler nicht triviale Dinge zu tun, da sich der Rest des Programms in einem unbekannten Zustand befindet. Die meisten Signalhandler setzen nur ein Flag, das später an anderer Stelle im Programm überprüft und behandelt wird.

Grund für den nicht automatischen Neustart des Systemaufrufs:

Stellen Sie sich eine Anwendung vor, die durch den blockierenden und unterbrechungsfreien recv() Systemaufruf Daten von einem Socket empfängt . In unserem Szenario sind die Daten sehr langsam und das Programm befindet sich lange in diesem Systemaufruf. Dieses Programm verfügt über einen Signalhandler SIGINT, der ein Flag setzt (das an anderer Stelle ausgewertet wird) und festlegt SA_RESTART, dass der Systemaufruf automatisch neu gestartet wird. Stellen Sie sich vor, das Programm recv()wartet auf Daten. Es kommen jedoch keine Daten an. Der Systemaufruf blockiert. Das Programm fängt jetzt ctrl- cvom Benutzer. Der Systemaufruf wird unterbrochen und der Signalhandler, der gerade das Flag setzt, wird ausgeführt. Dann recv()wird neu gestartet und wartet noch auf Daten. Die Ereignisschleife steckt fest recv()und hat keine Möglichkeit, das Flag auszuwerten und das Programm ordnungsgemäß zu beenden.

Mit SA_RESTARTnicht gesetzt:

Wenn das obige Szenario SA_RESTARTnicht festgelegt ist, wird recv()es empfangen, EINTRanstatt neu gestartet zu werden. Der Systemaufruf wird beendet und kann somit fortgesetzt werden. Natürlich sollte das Programm dann (so früh wie möglich) das Flag (vom Signalhandler gesetzt) ​​überprüfen und aufräumen oder was auch immer es tut.

Chaos
quelle
Ein weiterer Gesichtspunkt, der es wert sein könnte, hinzugefügt zu werden: skarnet.org/software/skalibs/libstddjb/safewrappers.html . Schließlich heißt es dasselbe (wenn auch impliziter), außer dass Sie in Ihrer Antwort davon ausgehen, dass es möglicherweise überhaupt keine Auszeit gibt.
Hibou57
2
Selbst mit SA_RESTARTset werden nicht alle Systemaufrufe automatisch neu gestartet. Zum Beispiel startet Linux nicht neu msgsnd()oder msgrcv().
Andrew Henle
5

Richard Gabriel hat einen Artikel The Rise of 'Worse is Better' geschrieben, in dem die Wahl des Designs hier in Unix erörtert wird:

Zwei berühmte Leute, einer vom MIT und einer aus Berkeley (die jedoch an Unix arbeiten), trafen sich einmal, um Probleme mit dem Betriebssystem zu besprechen. Die Person vom MIT kannte sich mit ITS (dem MIT AI Lab-Betriebssystem) aus und hatte die Unix-Quellen gelesen. Er war daran interessiert, wie Unix das Problem des PC-Verlierers löste. Das PC-Verliererproblem tritt auf, wenn ein Benutzerprogramm eine Systemroutine aufruft, um eine lange Operation auszuführen, die möglicherweise einen signifikanten Status aufweist, z. B. E / A-Puffer. Wenn während des Vorgangs ein Interrupt auftritt, muss der Status des Anwenderprogramms gespeichert werden. Da der Aufruf der Systemroutine normalerweise eine einzelne Anweisung ist, erfasst der PC des Anwenderprogramms den Status des Prozesses nicht angemessen. Die Systemroutine muss entweder zurücktreten oder vorwärts drücken. Das Richtige ist, den Benutzerprogramm-PC zurückzusetzen und auf die Anweisung zurückzusetzen, die die Systemroutine aufgerufen hat, damit beispielsweise die Wiederaufnahme des Benutzerprogramms nach dem Interrupt wieder in die Systemroutine eingeht. Es wird genanntPC loser-ingweil der PC dazu gezwungen wird loser mode, wobei "Verlierer" der liebevolle Name für "Benutzer" am MIT ist.

Der MIT-Mitarbeiter sah keinen Code, der diesen Fall behandelte, und fragte den New Jersey-Mitarbeiter, wie das Problem behandelt wurde. Der Typ aus New Jersey sagte, dass die Unix-Leute sich des Problems bewusst waren, aber die Lösung bestand darin, dass die Systemroutine immer beendet wurde, aber manchmal wurde ein Fehlercode zurückgegeben, der signalisierte, dass die Systemroutine ihre Aktion nicht abgeschlossen hatte. Ein korrektes Benutzerprogramm musste dann den Fehlercode überprüfen, um festzustellen, ob die Systemroutine einfach erneut versucht werden sollte. Der MIT-Typ mochte diese Lösung nicht, weil sie nicht das Richtige war.

Der Typ aus New Jersey sagte, dass die Unix-Lösung richtig sei, weil die Designphilosophie von Unix einfach und das Richtige zu komplex sei. Außerdem könnten Programmierer diesen zusätzlichen Test und diese Schleife leicht einfügen. Der MIT-Mitarbeiter wies darauf hin, dass die Implementierung einfach, die Schnittstelle zur Funktionalität jedoch komplex sei. Der Typ aus New Jersey sagte, dass unter Unix der richtige Kompromiss ausgewählt wurde - nämlich die Einfachheit der Implementierung sei wichtiger als die Einfachheit der Benutzeroberfläche.

Brad Schöning
quelle
1
Zwei berühmte Leute betreten eine Bar - eine vom MIT, eine aus Berkeley und eine aus New Jersey. Huh? Mir ist klar, dass es ein Zitat ist, aber können Sie es klarstellen? Der letzte Absatz ist auch ein bisschen durcheinander - die Unix-Lösung war richtig, weil das Richtige für Unix zu komplex war und sie es daher nicht implementiert haben.
G-Man sagt "Reinstate Monica"
Minimum Viable Product ist ein verwandtes Konzept. Die Unix-Lösung zur Verwendung von EINTR war praktikabel und bot Einfachheit in der hoch portablen OS-Codebasis. Die Handhabung von EINTR, die an den Benutzercode delegiert ist, ist einfach (nur erneut versuchen) und dennoch lästig.
Brad Schoening