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

Zeitverschwendung von execv() und fork()

Nicht mehr. Es gibt etwas namens COW (Copy On Write), nur wenn einer der beiden Prozesse (Parent/Child) versucht, auf eine gemeinsam genutzte Datei zu schreiben, wird diese kopiert.

Früher:
Die fork() Der Systemaufruf kopierte den Adressraum des aufrufenden Prozesses (den Elternprozess), um einen neuen Prozess (den Kindprozess) zu erstellen. Das Kopieren des Adressraums des Elternprozesses in den Kindprozess war der teuerste Teil des fork() Betrieb.

Jetzt:
Ein Anruf bei fork() folgt häufig fast unmittelbar ein Aufruf von exec() im Kindprozess, der das Gedächtnis des Kindes durch ein neues Programm ersetzt. Dies ist zum Beispiel das, was die Shell normalerweise tut. In diesem Fall wird die Zeit, die für das Kopieren des Adressraums des Elternprozesses aufgewendet wird, weitgehend verschwendet, da der Kindprozess vor dem Aufruf von exec() nur sehr wenig Speicher verwendet .

Aus diesem Grund nutzten spätere Versionen von Unix die virtuelle Speicherhardware, um es dem Eltern- und dem Kind zu ermöglichen, den in ihren jeweiligen Adressräumen abgebildeten Speicher gemeinsam zu nutzen, bis einer der Prozesse ihn tatsächlich ändert. Diese Technik wird als Copy-on-Write bezeichnet . Dazu auf fork() Der Kernel würde anstelle des Inhalts der zugeordneten Seiten die Adressraumzuordnungen von der übergeordneten zur untergeordneten Seite kopieren und gleichzeitig die jetzt gemeinsam genutzten Seiten als schreibgeschützt markieren. Wenn einer der beiden Prozesse versucht, auf eine dieser gemeinsam genutzten Seiten zu schreiben, nimmt der Prozess einen Seitenfehler. An diesem Punkt erkennt der Unix-Kernel, dass die Seite wirklich eine „virtuelle“ oder „Copy-on-Write“-Kopie war, und erstellt daher eine neue, private, beschreibbare Kopie der Seite für den fehlerhaften Prozess. Auf diese Weise werden die Inhalte einzelner Seiten erst dann kopiert, wenn tatsächlich darauf geschrieben wird. Diese Optimierung ergibt fork() gefolgt von einem exec() im Kind viel billiger:Das Kind muss wahrscheinlich nur eine Seite (die aktuelle Seite seines Stapels) kopieren, bevor es exec() aufruft .


Was ist der Vorteil, der durch die Verwendung dieser Kombination (anstelle einer anderen Lösung) erreicht wird, der dazu führt, dass die Leute sie immer noch verwenden, obwohl wir Verschwendung haben?

Sie müssen irgendwie einen neuen Prozess erstellen. Es gibt nur sehr wenige Möglichkeiten für ein Userspace-Programm, dies zu erreichen. POSIX hatte früher vfork() neben fork() , und einige Systeme haben möglicherweise ihre eigenen Mechanismen, wie z. B. den Linux-spezifischen clone() , aber seit 2008 spezifiziert POSIX nur noch fork() und die posix_spawn() Familie. Die fork + exec Die Route ist traditioneller, gut bekannt und hat wenige Nachteile (siehe unten). Die posix_spawn Familie ist als Sonderzweck konzipiert Ersatz für die Verwendung in Kontexten, die fork() Schwierigkeiten bereiten; Einzelheiten finden Sie im Abschnitt "Begründung" der Spezifikation.

Dieser Auszug aus der Linux-Manpage für vfork() kann aufschlussreich sein:

Unter Linux, fork (2) wird unter Verwendung von Copy-on-Write-Seiten implementiert, sodass die einzige Strafe durch fork entsteht (2) ist die Zeit und der Speicherplatz, die zum Duplizieren der Seitentabellen des Elternteils und zum Erstellen einer eindeutigen Aufgabenstruktur für das Kind erforderlich sind . In den schlechten alten Zeiten jedoch ein fork (2) würde eine vollständige Kopie des Datenraums des Anrufers erfordern, oft unnötig, da normalerweise unmittelbar danach ein exec (3) ist fertig. Daher hat BSD für mehr Effizienz den vfork eingeführt () Systemaufruf, der den Adressraum des übergeordneten Prozesses nicht vollständig kopiert, sondern den Speicher und den Kontrollthread des übergeordneten Prozesses bis zu einem Aufruf von execve ausgeliehen hat (2) oder ein Exit stattgefunden hat. Der übergeordnete Prozess wurde angehalten, während der untergeordnete Prozess seine Ressourcen verwendete. Die Verwendung von vfork () war knifflig:Beispielsweise hing das Nichtmodifizieren von Daten im übergeordneten Prozess davon ab, zu wissen, welche Variablen in einem Register gespeichert sind.

(Hervorhebung hinzugefügt)

Daher ist Ihre Besorgnis über Verschwendung für moderne Systeme (nicht auf Linux beschränkt) nicht begründet, aber es war historisch gesehen tatsächlich ein Problem, und es gab tatsächlich Mechanismen, die entwickelt wurden, um dies zu vermeiden. Heutzutage sind die meisten dieser Mechanismen veraltet.


Eine andere Antwort lautet:

In den schlechten alten Zeiten würde ein Fork(2) jedoch erfordern, dass eine vollständige Kopie des Datenraums des Aufrufers erstellt wird, oft unnötig, da normalerweise unmittelbar danach ein exec(3) ausgeführt wird.

Offensichtlich sind die schlechten alten Tage einer Person viel jünger als andere sich erinnern.

Die ursprünglichen UNIX-Systeme hatten nicht den Speicher zum Ausführen mehrerer Prozesse und sie hatten keine MMU, um mehrere Prozesse im physischen Speicher betriebsbereit im selben logischen Adressraum zu halten:Sie lagerten Prozesse auf die Festplatte aus, die dies nicht waren läuft gerade.

Der Fork-Systemaufruf war fast vollständig dasselbe wie das Auslagern des aktuellen Prozesses auf die Festplatte, mit Ausnahme des Rückgabewerts und für nicht Ersetzen der verbleibenden In-Memory-Kopie durch Austauschen in einem anderen Prozess. Da Sie den übergeordneten Prozess sowieso austauschen mussten, um den untergeordneten Prozess auszuführen, verursachte fork+exec keinen Overhead.

Es stimmt, dass es eine Zeit gab, in der fork+exec umständlich war:als es MMUs gab, die eine Zuordnung zwischen logischem und physischem Adressraum bereitstellten, aber Seitenfehler nicht genügend Informationen enthielten, die Copy-on-Write und eine Reihe anderer virtueller - Speicher-/Nachfrage-Paging-Schemata waren machbar.

Diese Situation war schmerzhaft genug, nicht nur für UNIX, dass die Seitenfehlerbehandlung der Hardware angepasst wurde, um ziemlich schnell "wiederholbar" zu werden.


Linux
  1. Benutzer- und Kernelzeit eines laufenden Prozesses abrufen?

  2. Befehle zum Neustarten und Herunterfahren von Linux

  3. Linux-Prozesszustände

  4. Unterschied zwischen CLOCK_REALTIME und CLOCK_MONOTONIC?

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

NTP-Server und Best Practices

So finden und töten Sie den Zombie-Prozess in Linux

So unterbrechen Sie einen Prozess und setzen ihn später in Linux fort

So stellen Sie Datum, Uhrzeit und Zeitzone in RHEL 8 ein

So finden Sie das Installationsdatum und die Uhrzeit des Linux-Betriebssystems

So stellen Sie Datum und Uhrzeit unter Linux ein