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

C-Dateizeiger ändert sich nach Fork und (fehlgeschlagener) Ausführung

Danke an Jonathan Leffler, der uns in die richtige Richtung weist.

Obwohl Ihr Programm für mich unter CentOS 7 / GCC 4.8.5 / GLIBC 2.17 nicht das gleiche unerwartete Verhalten erzeugt, ist es plausibel, dass Sie ein anderes Verhalten beobachten. Das Verhalten Ihres Programms ist tatsächlich undefiniert nach POSIX (auf das Sie sich für fork verlassen ). Hier sind einige Auszüge aus dem entsprechenden Abschnitt (Hervorhebung hinzugefügt):

Auf eine offene Dateibeschreibung kann über einen Dateideskriptor zugegriffen werden, der unter Verwendung von Funktionen wie open() erstellt wird oder pipe() , oder durch einen Stream, der mit Funktionen wie fopen() erstellt wird oder popen() .Entweder ein Dateideskriptor oder ein Stream wird in der Openfile-Beschreibung, auf die er sich bezieht, als "Handle" bezeichnet; eine geöffnete Dateibeschreibung kann mehrere Handles haben.

[...]

Das Ergebnis von Funktionsaufrufen, an denen ein beliebiges Handle (das „aktive Handle“) beteiligt ist, wird an anderer Stelle in diesem Band von POSIX.1-2017 definiert, aber wenn zwei oder mehr Handles verwendet werden und eines davon ein Stream ist, muss die Anwendung sicherstellen, dass ihre Aktionen werden wie unten beschrieben koordiniert. Wenn dies nicht getan wird, ist das Ergebnis undefiniert .

[...]

Damit ein Handle zum aktiven Handle wird, muss die Anwendung sicherstellen, dass die nachstehenden Aktionen zwischen der letzten Verwendung des Handles (dem aktuellen aktiven Handle) und der ersten Verwendung des zweiten Handles (dem zukünftigen aktiven Handle) durchgeführt werden. Der zweite Griff wird dann zum aktiven Griff. [...]

Die Handles müssen sich nicht im selben Prozess befinden, damit diese Regeln gelten.

Beachten Sie, dass nach einem fork() , sind zwei Handles vorhanden, wo vorher eines existierte. Die Anwendung muss sicherstellen, dass, wenn jemals auf beide Handles zugegriffen werden kann, sie beide in einem Zustand sind, in dem das andere zuerst das aktive Handle werden könnte. [Vorbehaltlich der vorangehenden Qualifikation muss die] Bewerbung auf einen fork() vorbereiten genau so, als ob es sich um eine Änderung des aktiven Handles handeln würde. (Wenn die einzige Aktion, die von einem der Prozesse ausgeführt wird, eine der exec-Funktionen ist oder_exit() (nicht exit() ), wird in diesem Prozess niemals auf das Handle zugegriffen. )

Für das erste Handle gilt die erste anwendbare Bedingung unten. [Eine beeindruckend lange Liste von Alternativen, die nicht auf die Situation des OP zutreffen ...]

  • Wenn der Stream in einem Modus geöffnet ist, der das Lesen zulässt, und die zugrunde liegende offene Dateibeschreibung auf ein Gerät verweist, das suchen kann, muss die Anwendung entweder einen fflush() ausführen , oder der Stream wird geschlossen.

Für das zweite Handle:

  • Wenn ein vorheriges aktives Handle von einer Funktion verwendet wurde, die den Datei-Offset explizit geändert hat, außer wie oben für das erste Handle erforderlich, muss die Anwendung einen lseek() ausführen oder fseek() (entsprechend dem Grifftyp) an einer geeigneten Stelle.

Damit das Programm des OP auf den gleichen Stream sowohl im Elternteil als auch im Kindteil zugreifen kann, verlangt POSIX, dass der Elternteil fflush() stdin vor dem Forking, und dass das Kind fseek() es nach dem Start. Dann, nachdem darauf gewartet wurde, dass das Kind beendet wird, muss das Elternteil fseek() der Strom. Angesichts der Tatsache, dass wir wissen, dass die Exekutive des Kindes versagen wird, kann die Anforderung für das ganze Spülen und Suchen vermieden werden, indem das Kind _exit() verwendet (der nicht auf den Stream zugreift) anstelle von exit() .

Die Einhaltung der Bestimmungen von POSIX ergibt Folgendes:

Wenn diese Regeln befolgt werden, müssen Implementierungen unabhängig von der Reihenfolge der verwendeten Handles sicherstellen, dass eine Anwendung, selbst wenn sie aus mehreren Prozessen besteht, korrekte Ergebnisse liefert:Beim Schreiben dürfen keine Daten verloren gehen oder dupliziert werden, und alle Daten müssen der Reihe nach geschrieben werden, außer als angefordert durch sucht.

Es ist jedoch erwähnenswert, dass

Ob und unter welchen Bedingungen alle Eingaben genau einmal gesehen werden, ist implementierungsabhängig.

Ich weiß, dass es etwas unbefriedigend sein kann, nur zu hören, dass Ihre Erwartungen an das Programmverhalten nicht durch die relevanten Standards gerechtfertigt sind, aber das ist wirklich alles. Die übergeordneten und untergeordneten Prozesse haben einige relevante gemeinsame Daten in Form einer gemeinsamen offenen Dateibeschreibung (mit der sie separate Handles verknüpft haben), und das scheint wahrscheinlich das Vehicle zu sein für das unerwartete (und undefinierte) Verhalten, aber es gibt keine Grundlage für die Vorhersage des spezifischen Verhaltens, das Sie sehen, noch für das unterschiedliche Verhalten, das ich für dasselbe Programm sehe.


Linux
  1. Dateiberechtigungen und Speichern?

  2. Ausgabe umleiten und leiten?

  3. Ungültige cPanel-Lizenzdatei nach Änderung des Hostnamensfehlers

  4. Installation und Konfiguration von vsFTPD

  5. Der Unterschied zwischen fork(), vfork(), exec() und clone()

Lernen und verwenden Sie die Systemaufrufe fork(), vfork(), wait() und exec() auf Linux-Systemen

Unix - Kopf UND Ende der Datei

Cron nach Änderung der Crontab-Datei neu starten?

Linux ext4 stellt Datei- und Verzeichniszugriffsrechte nach fehlerhafter Sicherung/Wiederherstellung wieder her

Das System verweigert SSH und bleibt nach der systemd-Installation beim Hochfahren hängen

Systemd-Unit-Datei - WantedBy und After