Was ist der Unterschied zwischen fork () und vfork ()?

13

Ich möchte den Unterschied zwischen fork () und vfork () im Detail verstehen. Ich konnte die Manpage nicht vollständig verdauen.

Ich möchte auch einen Kommentar meiner Kollegen klarstellen: " In aktuellem Linux gibt es kein vfork (), auch wenn Sie es aufrufen, wird es intern fork () aufrufen ."

Sen
quelle

Antworten:

24

Manpages sind in der Regel knappe Referenzdokumente. Wikipedia ist ein besserer Ort für konzeptionelle Erklärungen.

Fork dupliziert einen Prozess: Es wird ein untergeordneter Prozess erstellt, der fast identisch mit dem übergeordneten Prozess ist (der offensichtlichste Unterschied besteht darin, dass der neue Prozess eine andere Prozess-ID hat). Insbesondere muss fork (konzeptionell) den gesamten Speicher des übergeordneten Prozesses kopieren.

Da dies ziemlich kostspielig ist, wurde vfork erfunden, um einen allgemeinen Sonderfall zu behandeln, bei dem die Kopie nicht erforderlich ist. Oft ist das erste, was der untergeordnete Prozess tut, ein neues Programm-Image zu laden. Das passiert also:

if (fork()) {
    # parent process …
} else {
    # child process (with a new copy of the process memory)
    execve("/bin/sh", …);  # discard the process memory
}

Der execveAufruf lädt ein neues ausführbares Programm, und dies ersetzt den Code und den Datenspeicher des Prozesses durch den Code der neuen ausführbaren Datei und einen neuen Datenspeicher. Die gesamte von erstellte Speicherkopie forkwar also alles für nichts.

So wurde der vforkRuf erfunden. Es wird keine Kopie des Speichers erstellt. Daher vforkist es billig, aber es ist schwierig zu verwenden, da Sie sicherstellen müssen, dass Sie nicht auf den Stapel- oder Heapspeicher des Prozesses im untergeordneten Prozess zugreifen. Beachten Sie, dass auch das Lesen ein Problem sein kann, da der übergeordnete Prozess weiterhin ausgeführt wird. Dieser Code ist beispielsweise fehlerhaft (funktioniert möglicherweise nicht, je nachdem, ob das Kind oder das Elternteil zuerst eine Zeitscheibe erhält):

if (vfork()) {
    # parent process
    cmd = NULL; # modify the only copy of cmd
} else {
    # child process
    execve("/bin/sh", "sh", "-c", cmd, (char*)NULL);  # read the only copy of cmd
}

Seit der Erfindung von vfork wurden bessere Optimierungen erfunden. Die meisten modernen Systeme, einschließlich Linux, verwenden eine Form von Copy-on-Write , bei der die Seiten im Prozessspeicher nicht zum Zeitpunkt des forkAufrufs kopiert werden , sondern später, wenn das über- oder untergeordnete Element zum ersten Mal auf die Seite schreibt. Das heißt, jede Seite beginnt als freigegeben und bleibt freigegeben, bis ein Prozess auf diese Seite schreibt. Der schreibende Prozess erhält eine neue physische Seite (mit derselben virtuellen Adresse). Copy-on-Write macht vfork zumeist unbrauchbar, da forkin den Fällen, in denen vforkes brauchbar wäre , keine Kopie erstellt wird .

Linux behält vfork bei. Der forkSystemaufruf muss weiterhin eine Kopie der virtuellen Speichertabelle des Prozesses erstellen, auch wenn der tatsächliche Speicher nicht kopiert wird. vforkbraucht das nicht einmal zu tun. Die Leistungsverbesserung ist in den meisten Anwendungen vernachlässigbar.

Gilles 'SO - hör auf böse zu sein'
quelle
1
Vielen Dank für diese brillante Antwort. Während der Ausführung von fork () erhält das Kind sowieso eine neue Prozess-ID und den zugehörigen virtuellen Speicherbereich. Warum sollte es dann immer noch eine Kopie der virtuellen Speichertabelle des Prozesses erstellen? Ich bin in diesem Teil nicht klar.
Sen
@Sen: forkmuss eine separate Zuordnung des virtuellen Speichers erstellen, damit nachfolgende Copy-on-Write-Kopien nur einen der beiden Prozesse betreffen.
Gilles 'SO- hör auf böse zu sein'
Sind Sie sicher, dass der übergeordnete Prozess ausgeführt wird?
Qbolec
2

Die fork()und vfork()Syscalls sind unterschiedlich.

Der fork()Syscall generiert zwei identische Prozesse mit separatem Speicher. Der vfork()Syscall generiert zwei Prozesse, die sich denselben Speicher teilen.

Mit vfork()dem Elternteil wird gewartet, bis das Kind abbricht. Das übergeordnete Element erbt von den Variablen, die das Programm gemeinsam nutzt. Nachdem das untergeordnete Element aufgerufen wurde, werden alle im untergeordneten Element geänderten Variablen weiterhin im übergeordneten Element geändert.

Weitere Informationen finden Sie hier

Yogeesh HT
quelle