Warum sollte ein Kind einer vfork oder fork _exit () anstelle von exit () aufrufen?

12

Aus der Manpage von vfork():

vfork () unterscheidet sich von fork () dadurch, dass das übergeordnete Element angehalten wird, bis das untergeordnete Element einen Aufruf zum Ausführen von (2) oder _exit (2) ausführt. Das Kind teilt den gesamten Speicher mit seinem Elternteil, einschließlich des Stapels, bis execve () vom Kind ausgegeben wird. Das Kind darf nicht von der aktuellen Funktion zurückkehren oder exit () aufrufen, sondern darf _exit () aufrufen.

Warum sollte das Kind einen verwenden, _exit()anstatt nur anzurufen exit()? Ich hoffe, dass dies sowohl für vfork()als auch gilt fork().

Sen.
quelle
2
Wenn Sie an einer früheren Diskussion über die Ontizität von Unix C-API-Aufrufen
interessiert sind

Antworten:

11

Wie bereits erwähnt , kann vforkder untergeordnete Prozess nicht auf den Speicher des übergeordneten Elements zugreifen. exitist eine C-Bibliotheksfunktion (deshalb wird sie oft als geschriebenexit(3) ). Es führt verschiedene Bereinigungsaufgaben aus, z. B. das Löschen und Schließen von C-Streams (die Dateien werden über in deklarierte Funktionen geöffnet stdio.h) und das Ausführen von benutzerdefinierten Funktionen, die bei registriert sind atexit. Alle diese Aufgaben umfassen das Lesen und Schreiben in den Prozessspeicher.

_exitwird ohne Bereinigung beendet. Es handelt sich direkt um einen Systemaufruf (weshalb er als geschrieben wird _exit(2)), der normalerweise implementiert wird, indem die Systemaufrufnummer in ein Prozessorregister gestellt und ein bestimmter Prozessorbefehl ausgeführt wird (Verzweigung zum Systemaufruf-Handler). Dies muss nicht auf den Prozessspeicher zugreifen, daher ist es sicher, dies danach zu tun vfork.

Danach forkgibt es keine solche Einschränkung mehr: Der Eltern- und der Kindprozess sind jetzt völlig autonom.

Gilles 'SO - hör auf böse zu sein'
quelle
vfork erlaubt dem untergeordneten Prozess nicht, auf den Speicher des Elternteils zuzugreifen ? Aber ich dachte, sie teilen sich den gleichen Adressraum, damit das Kind auf den Adressraum der Eltern zugreifen kann. War dieses Verständnis falsch?
Sen
Nach dem Fork gibt es keine solche Einschränkung: Der Eltern- und Kindprozess ist jetzt vollständig autonom. Heißt das, ich kann einen exit () von einem Kind einer Gabel aufrufen ?
Sen
1
@Sen: Das Kind darf nicht auf den Speicher des Elternteils zugreifen. Wenn Sie es versuchen, funktioniert es möglicherweise, da der Kernel Sie nicht schützt. Aber der Effekt ist möglicherweise nicht das, was Sie beabsichtigen. Wenn Ihr Compiler beispielsweise beschließt, einen Wert in einem Register zu belassen, wird er vom übergeordneten Prozess nicht angezeigt.
Gilles 'SO - hör auf böse zu sein'
@Sen: Nach einer Abzweigung können Sie den Exit oder eine andere Funktion aufrufen. Jeder Prozess startet nach einem Fork (unter Linux wird sogar der erste Prozess initvom Kernel gegabelt).
Gilles 'SO - hör auf böse zu sein'
3

exitFühren Sie zusätzliche Bereinigungen durch, z. B. das Aufrufen von Funktionen, die registriert sind, indem atexitauf Daten außerhalb des kopierten Teils zugegriffen wird. _exitführt syscall direkt ohne Bereinigung durch, außer im Kernel.

Maciej Piechotka
quelle
... und es sollte beachtet werden, dass fork () alles kopiert, so dass Sie möglicherweise exit () aufrufen können und definitiv von der aktuellen Funktion zurückkehren können.
Derobert
3

Sie haben den untergeordneten Aufruf _exit (), um zu vermeiden, dass stdio (oder andere) Puffer geleert werden, wenn der untergeordnete Prozess beendet wird. Da der untergeordnete Prozess eine exakte Kopie des übergeordneten Prozesses darstellt, enthält der untergeordnete Prozess immer noch die Puffer von <stdio.h> in "stdout" oder "stderr". Sie können (und werden zu ungünstigen Zeiten) doppelte Ausgaben erhalten, indem Sie exit () aufrufen, eine von den Atexit-Handlern des untergeordneten Prozesses und eine von den übergeordneten, wenn die Puffer im übergeordneten Prozess voll sind und geleert werden.

Mir ist klar, dass sich die obige Antwort auf die Besonderheiten von stdio.h konzentriert, aber diese Idee überträgt sich wahrscheinlich auf andere gepufferte E / A, genau wie eine der obigen Antworten angibt.

Bruce Ediger
quelle
1

exit(): - führt eine Bereinigungsaufgabe aus, z. B. das Schließen der E / A-Streams und vieler, und kehrt dann zum Kernel zurück. _exit(): - kommt direkt zum Kernel (keine Bereinigungsaufgabe ausführen).

fork() : Sowohl Eltern als auch Kind haben unterschiedliche Dateitabellen, sodass die vom Kind vorgenommenen Änderungen keine Auswirkungen auf die Umgebungsparameter des Elternteils haben und umgekehrt.

vfork(): Sowohl Eltern als auch Kind verwenden dieselbe Dateitabelle, sodass sich die von Kind vorgenommene Änderung auf die Umgebungsparameter des Elternteils auswirkt. Beispiel: Eine Variable var=10, die jetzt var++von einem Kind und dann von einem Elternteil ausgeführt wird. Sie können den Effekt auch var++in der Ausgabe des Elternteils sehen.

Wie gesagt, wenn Sie exit()in verwenden, vfork()ist die gesamte E / A bereits geschlossen. Selbst wenn das übergeordnete Element korrekt ausgeführt wird, können Sie nicht die richtige Ausgabe erhalten, da alle Variablen gelöscht und alle Streams geschlossen werden.

user2670535
quelle