Warum soll vfork () verwendet werden, wenn der untergeordnete Prozess unmittelbar nach der Erstellung exec () oder exit () aufruft?


11

Betriebssystemkonzepte und APUE sagen

Mit vfork () wird der übergeordnete Prozess angehalten und der untergeordnete Prozess verwendet den Adressraum des übergeordneten Prozesses. Da vfork () Copy-on-Write nicht verwendet, werden die geänderten Seiten für den übergeordneten Prozess sichtbar, sobald der untergeordnete Prozess Seiten des Adressraums des übergeordneten Elements ändert, sobald er fortgesetzt wird. Daher muss vfork () mit Vorsicht verwendet werden, um sicherzustellen, dass der untergeordnete Prozess den Adressraum des übergeordneten Prozesses nicht ändert.

vfork () soll verwendet werden, wenn der untergeordnete Prozess unmittelbar nach der Erstellung exec () oder exit () aufruft.

Wie soll ich den letzten Satz verstehen?

Wenn ein untergeordneter Prozess, der durch vfork()Aufrufe erstellt wurde exec(), exec()den Adressraum des übergeordneten Prozesses nicht durch Laden des neuen Programms ändert ?

Wenn ein untergeordneter Prozess, der durch vfork()Aufrufe erstellt wurde exit(), exit()den Adressraum des übergeordneten Prozesses beim Beenden des untergeordneten Prozesses nicht ändert?

Ich bevorzuge Linux.

Vielen Dank.

Antworten:


15

Wenn ein untergeordneter Prozess, der durch vfork()Aufrufe erstellt wurde exec(), exec()den Adressraum des übergeordneten Prozesses nicht durch Laden des neuen Programms ändert ?

Nein, exec()bietet einen neuen Adressraum für das neue Programm. Der übergeordnete Adressraum wird nicht geändert. Siehe zum Beispiel die Diskussion der execFunktionen in POSIX und die Linux- execve()Manpage .

Wenn ein von vfork () erstellter untergeordneter Prozess exit () aufruft, ändert exit () beim Beenden des untergeordneten Prozesses nicht den Adressraum des übergeordneten Prozesses?

Einfach exit()- es werden Exit-Hooks ausgeführt, die vom laufenden Programm (einschließlich seiner Bibliotheken) installiert wurden. vfork()ist restriktiver; somit auf Linux, beauftragt es die Verwendung von _exit()denen nicht nicht die Bereinigungs C Bibliothek Funktionen aufrufen.

vfork()stellte sich als ziemlich schwierig heraus, richtig zu machen; Es wurde in aktuellen Versionen des POSIX-Standards entfernt und posix_spawn()sollte stattdessen verwendet werden.

Wenn Sie jedoch nicht wirklich wissen, was Sie tun, sollten Sie entweder oder nicht verwenden . bleib bei guten alten und .vfork()posix_spawn()fork()exec()

Die oben verlinkte Linux-Manpage bietet mehr Kontext:

In den schlechten alten Zeiten fork(2) würde es jedoch erforderlich sein, eine vollständige Kopie des Datenraums des Anrufers zu erstellen, oft unnötig, da dies normalerweise unmittelbar danach exec(3)erfolgt. Aus vfork() Gründen der Effizienz führte BSD den Systemaufruf ein, der den Adressraum des übergeordneten Prozesses nicht vollständig kopierte, sondern den Speicher und den Kontrollthread des übergeordneten Prozesses auslieh, bis ein Aufruf execve(2)oder ein Exit erfolgte. Der übergeordnete Prozess wurde angehalten, während das untergeordnete Element seine Ressourcen verwendete. Die Verwendung von vfork()war schwierig: Zum Beispiel hing das Nicht-Ändern von Daten im übergeordneten Prozess davon ab, zu wissen, welche Variablen in einem Register gespeichert waren.


Vielen Dank. "exec () stellt einen neuen Adressraum für das neue Programm bereit;" Ist das normale Verhalten von exec (), ein Programm in den Adressraum des Prozesses zu laden? Ich habe in den beiden Links nicht gefunden, wo es normalerweise oder speziell für vfork () einen neuen Adressraum erstellt.
Tim

1
Was lustig ist, ist, dass vfork () jetzt gegen fast alles andere gewinnt. Es ist lächerlich schneller als fork (), wenn Sie ein Gigabyte beschreibbaren Speicher haben.
Joshua

2
Bitte sag den Leuten nicht, dass sie es benutzen sollen posix_spawn. Es ist erheblich schwieriger, korrekten Code posix_spawnmit einfachem zu schreiben, als mit einfachem altem Code. forkWenn Sie es versuchen, stoßen Sie möglicherweise auf die Mauer, dass es keine Dateiaktion oder kein Attribut gibt, das das tut, was Sie zwischen forkund tun müssen exec. Und es ist nicht garantiert, dass es eine vfork-ähnliche Effizienz hat, so dass es nicht einmal das Problem löst, das die Leute wollen, dass es löst.
zwol

1
@zwol: Das ist wirklich ein schlechter Rat. Während posix_spawnIhnen möglicherweise die gewünschte Funktionalität fehlt (Sie können dies über ein zwischengeschaltetes Hilfsprogramm lösen, das in C oder einem Inline-on-cmdline-Shell-Skript geschrieben ist), vforkruft jeder Versuch, das zu erreichen, was Sie wollen, gefährliches undefiniertes Verhalten hervor. Die Spezifikation für vforkerlaubt es nicht, zufällige Funktionen aufzurufen, um den Status festzulegen, den das Kind zuvor erben soll execve, und Versuche, dies zu tun, können den Status des Elternteils beschädigen.
R .. GitHub STOP HELPING ICE

1
@Joshua: Eine moderne Implementierung von funktioniert posix_spawnungefähr genauso wie vforkunter den meisten Bedingungen. Diejenigen, bei denen es einen Unterschied gibt, sind in der Regel genau die Fälle, in denen dies vforksehr unsicher ist: Hier sind Signalhandler installiert, posix_spawndie verhindern müssen, dass das Kind vor der Ausführung ausgeführt wird.
R .. GitHub STOP HELPING ICE

4

Wenn Sie aufrufen vfork(), wird ein neuer Prozess erstellt, und dieser neue Prozess leiht das Prozessabbild des übergeordneten Prozesses mit Ausnahme des Stapels aus. Der untergeordnete Prozess erhält einen eigenen neuen Stapelstern, lässt jedoch die returnaufgerufene Funktion nicht zu vfork().

Während das untergeordnete Element ausgeführt wird, wird der übergeordnete Prozess blockiert, da das untergeordnete Element den Adressraum des übergeordneten Elements ausgeliehen hat.

Unabhängig davon, was Sie tun, ändert alles, was nur auf den Stapel zugreift, nur den privaten Stapel des Kindes. Wenn Sie jedoch globale Daten ändern, ändert dies die allgemeinen Daten und wirkt sich somit auch auf die übergeordneten Daten aus.

Dinge, die globale Daten ändern, sind z.

  • Aufruf von malloc () oder free ()

  • mit stdio

  • Ändern der Signaleinstellungen

  • Ändern von Variablen, die nicht lokal für die aufgerufene Funktion sind vfork().

  • ...

Sobald Sie anrufen _exit()(wichtig, nie anrufen exit()), wird das Kind beendet und die Kontrolle an das Elternteil zurückgegeben.

Wenn Sie eine Funktion aus der exec*()Familie aufrufen , wird ein neuer Adressraum mit neuem Programmcode, neuen Daten und einem Teil des Stapels vom übergeordneten Element erstellt (siehe unten). Sobald dies fertig ist, leiht das Kind den Adressraum nicht mehr vom Kind aus, sondern verwendet einen eigenen Adressraum.

Das Steuerelement wird an das übergeordnete Element zurückgegeben, da der Adressraum nicht mehr von einem anderen Prozess verwendet wird.

Wichtig: Unter Linux gibt es keine echte vfork()Implementierung. Linux implementiert eher vfork()basierend auf dem fork()1988 von SunOS-4.0 eingeführten Copy on Write- Konzept. Damit Benutzer glauben, dass sie es verwenden vfork(), richtet Linux nur gemeinsam genutzte Daten ein und setzt das übergeordnete Element aus, während das untergeordnete Element _exit()oder eine der exec*()Funktionen nicht aufgerufen hat .

Linux profitiert daher nicht von der Tatsache, dass ein Real vfork()keine Adressraumbeschreibung für das Kind im Kernel einrichten muss. Dies führt zu einem vfork(), der nicht schneller als ist fork(). Auf Systemen, die ein reales System implementieren vfork(), ist es in der Regel dreimal schneller als fork()und beeinflusst die Leistung von Shells, die vfork()- ksh93, die neuesten Bourne Shellund csh.

Der Grund, warum Sie niemals exit()vom vfork()ed-Kind anrufen sollten, ist, dass exit()stdio gelöscht wird, falls Daten aus der Zeit vor dem Anruf nicht gelöscht werden vfork(). Dies kann zu seltsamen Ergebnissen führen.

Übrigens: posix_spawn()wird zusätzlich implementiert vfork()und wird daher vfork()nicht aus dem Betriebssystem entfernt. Es wurde erwähnt, dass Linux nicht vfork()für verwendet posix_spawn().

Für den Stack gibt es nur wenige Dokumentationen. In der Solaris-Manpage heißt es Folgendes:

 The vfork() and vforkx() functions can normally be used  the
 same  way  as  fork() and forkx(), respectively. The calling
 procedure, however, should not return while running  in  the
 child's  context,  since the eventual return from vfork() or
 vforkx() in the parent would be to a  stack  frame  that  no
 longer  exists. 

Die Implementierung kann also tun, was sie will. Die Solaris-Implementierung verwendet gemeinsam genutzten Speicher für den Stapelrahmen des Funktionsaufrufs vfork(). Keine Implementierung gewährt dem übergeordneten Element Zugriff auf ältere Teile des Stapels.


4
Weder die GNU C-Bibliothek noch die musl C-Bibliothek werden posix_spawn()auf Linux implementiert vfork(). Beide implementieren es zusätzlich __clone().
JdeBP

1
@JdeBP: Weißt du, vfork()ruft einfach clone()richtig an? Es ist buchstäblich ein Einzeiler im Kernel.
Joshua

1
"Wichtig: Unter Linux gibt es keine echte vfork () - Implementierung." <- Dies ist nicht wahr und seit mindestens einem Jahrzehnt nicht mehr wahr. Wenn Ihr Shell-Benchmark keinen Leistungsunterschied zwischen vforkund forkunter Linux feststellt, stimmt etwas nicht.
zwol

1
Die zweite Hälfte dieser Antwort, die mit "Wichtig: Unter Linux gibt es keine echte vfork () - Implementierung" beginnt, ist größtenteils oder völlig falsch.
R .. GitHub STOP HELPING ICE

1
Bitte machen Sie keine Ansprüche ohne Überprüfung. Die aktuelle Bourne Shell kann mit und ohne vfork-Unterstützung kompiliert werden. Selbst wenn Sie der Meinung sind, dass die Debugging-Funktionen von Linux keine zuverlässigen Ergebnisse liefern können, können Sie die Ausführungszeiten für einen Konfigurationsaufruf mit und mit vfork in der Shell vergleichen. Ich verwende ein Konfigurationsskript mit 800 Tests. Unter Solaris benötigt die Bourne Shell, die vfork verwendet, insgesamt 30% weniger System-CPU-Zeit als das Gabelgehäuse. Unter Linux führt der gleiche Test zu weniger als 10% weniger System-CPU-Zeit. Unter Solaris ist es nicht 3x, da viele Compiler-Aufrufe enthalten sind.
schily
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.