Funktionieren von fork() in Linux gcc [duplicate]

Lesezeit: 5 Minuten

Benutzer-Avatar
Mor Eru

fork() erstellt einen neuen Prozess und der untergeordnete Prozess beginnt mit der Ausführung vom aktuellen Status des übergeordneten Prozesses.

Das ist das, wovon ich weiß fork() unter Linux.

Also entsprechend folgender Code:

int main() {
  printf("Hi");
  fork();
  return 0;
}

muss wie oben beschrieben nur einmal “Hi” drucken.

Aber beim Ausführen des Obigen unter Linux, kompiliert mit gcc, wird “Hi” gedruckt. zweimal.

Kann mir jemand erklären, was eigentlich bei der Nutzung passiert fork() und ob ich die funktionsweise verstanden habe fork() richtig?

  • Wenn Sie eine hinzufügen fflush(stdout); vor Ihrer Gabel wird es tun, was Sie von ihm erwartet haben.

    – Nategans

    18. August 2010 um 18:39 Uhr

Benutzer-Avatar
Paul Tomblin

(Einschließlich einiger Erklärungen aus einem Kommentar von Benutzer @Jack) Wenn Sie etwas auf die Standardausgabe “Standardausgabe” drucken (normalerweise Computermonitor, obwohl Sie es in eine Datei umleiten können), wird es zunächst im temporären Puffer gespeichert.

Beide Seiten der Verzweigung erben den nicht geleerten Puffer. Wenn also jede Seite der Verzweigung auf die return-Anweisung trifft und endet, wird sie zweimal geleert.

Bevor Sie forken, sollten Sie fflush(stdout); wodurch der Puffer geleert wird, damit das untergeordnete Element ihn nicht erbt.

stdout auf den Bildschirm (im Gegensatz zur Umleitung in eine Datei) wird tatsächlich durch Zeilenenden gepuffert, also wenn Sie fertig wären printf("Hi\n"); Sie hätten dieses Problem nicht gehabt, weil es den Puffer selbst geleert hätte.

  • Können Sie Ihre Antwort bitte ausführlich erläutern, Sir? Ich bin neu darin, C-Programme in gcc zu machen. Also ich kann deine Antwort nicht nachvollziehen!!

    – Mor Eru

    18. August 2010 um 14:43 Uhr

  • Sie sollten in der Lage sein, die Genauigkeit davon zu testen, indem Sie fflush(stdin)

    – Torak

    18. August 2010 um 14:43 Uhr

  • @Shyam: Wenn Sie etwas auf STDOUT drucken (normalerweise Computermonitor), wird es zunächst im temporären Puffer gespeichert. Wenn Sie einen Fork ausführen, wird dieser Puffer vom untergeordneten Element geerbt. Wenn der Puffer geleert wird, können Sie ihn von beiden Prozessen sehen. Wenn Sie fflush manuell verwenden, wird der Puffer bereinigt und das Kind erbt das nicht. Sie sehen dann nur einen Druck.

    – Jack

    18. August 2010 um 14:49 Uhr

  • @Jack, das ist so eine gute Erklärung. Stört es Sie, wenn ich es in meine Antwort einbeziehe?

    – Paul Tomblin

    18. August 2010 um 14:52 Uhr

  • Wenn stdout eine normale Datei ist, wird sie normalerweise blockgepuffert. Verschmelzen Sie stdout nicht mit einem tty.

    – William Pursel

    18. August 2010 um 14:59 Uhr

Benutzer-Avatar
Stefan

printf("Hi"); druckt nicht sofort das Wort “Hi” auf Ihren Bildschirm. Was es tut, ist das Füllen stdout Puffer mit dem Wort “Hi”, das dann angezeigt wird, sobald der Puffer “gespült” ist. In diesem Fall, stdout zeigt (vermutlich) auf Ihren Monitor. In diesem Fall wird der Puffer geleert, wenn er voll ist, wenn Sie das Leeren erzwingen oder (am häufigsten) wenn Sie ein Zeilenumbruchzeichen (“\n”) ausgeben. Da ist der Puffer noch voll wenn fork() aufgerufen wird, erben es sowohl der übergeordnete als auch der untergeordnete Prozess, und daher geben beide “Hi” aus, wenn sie den Puffer leeren. Wenn Sie anrufen fflush(stout); vor dem Aufruf von fork sollte es funktionieren:

int main() {
  printf("Hi");
  fflush(stdout);
  fork();
  return 0;
}

Alternativ, wie gesagt, wenn Sie einen Zeilenumbruch in Ihre einfügen printf es sollte auch funktionieren:

int main() {
  printf("Hi\n");
  fork();
  return 0;
}

  • Kann jemand erfahrener als ich in C bestätigen, dass der stdout-Puffer geleert wird, wenn ein Zeilenumbruchzeichen darin eingegeben wird? Ich habe dieses Wissen von irgendwoher, aber ich bin mir nicht 100% sicher.

    – Stefan

    18. August 2010 um 14:47 Uhr

  • C kennt 3 Puffermodi: unbuffered, fully buffered und line buffered. Aus C99 §7.19.3 (7): “[…] Beim anfänglichen Öffnen ist der Standardfehlerstrom nicht vollständig gepuffert; Die Standardeingabe- und Standardausgabestreams werden genau dann vollständig gepuffert, wenn festgestellt werden kann, dass sich der Stream nicht auf ein interaktives Gerät bezieht.”

    – schott

    18. August 2010 um 14:52 Uhr


Im Allgemeinen ist es sehr unsicher, offene Handles/Objekte von Bibliotheken auf beiden Seiten von fork() zu verwenden.

Dazu gehört die C-Standardbibliothek.

fork() macht aus einem zwei Prozesse, und keine Bibliothek kann erkennen, dass dies geschieht. Wenn also beide Prozesse weiterhin mit denselben Dateideskriptoren / Sockets usw. ausgeführt werden, haben sie jetzt unterschiedliche Zustände, teilen sich jedoch dieselben Dateihandles (technisch gesehen haben sie Kopien, aber dieselben zugrunde liegenden Dateien). Dadurch passieren schlimme Dinge.

Beispiele für Fälle, in denen fork() dieses Problem verursacht

  • stdio zB tty Input/Output, Pipes, Disc-Dateien
  • Sockets, die zB von einer Datenbank-Client-Bibliothek verwendet werden
  • Sockets, die von einem Serverprozess verwendet werden – was zu seltsamen Effekten führen kann, wenn ein Kind, das einen Socket bedient, zufällig ein Datei-Handle für einen anderen erbt – ist schwierig, diese Art der Programmierung richtig zu machen, siehe Apache-Quellcode für Beispiele.

So beheben Sie dies im allgemeinen Fall:

Entweder

a) Unmittelbar nach fork() rufen Sie exec() auf, möglicherweise auf derselben Binärdatei (mit den notwendigen Parametern, um die beabsichtigte Arbeit zu erreichen). Das ist sehr einfach.

b) Verwenden Sie nach dem Forken keine bestehenden offenen Handles oder Bibliotheksobjekte, die davon abhängen (das Öffnen neuer ist in Ordnung); Beenden Sie Ihre Arbeit so schnell wie möglich und rufen Sie dann _exit() (nicht exit() ) auf. Kehren Sie nicht von der Unterroutine zurück, die fork aufruft, da dies riskiert, C++-Destruktoren usw. aufzurufen, die den Dateideskriptoren des übergeordneten Prozesses schaden könnten. Dies ist mäßig einfach.

c) Nach dem Verzweigen irgendwie alle Objekte aufräumen und sie alle in einen gesunden Zustand bringen, bevor das Kind fortfährt. Schließen Sie zB zugrunde liegende Dateideskriptoren, ohne Daten zu löschen, die sich in einem Puffer befinden, der im Elternteil dupliziert ist. Das ist schwierig.

c) ist ungefähr das, was Apache tut.

printf() puffert. Haben Sie versucht, zu drucken stderr?

Technische Antwort:

Wenn Sie fork() verwenden, müssen Sie sicherstellen, dass exit() nicht zweimal aufgerufen wird (das Abfallen von main ist dasselbe wie das Aufrufen von exit()). Das untergeordnete Element (oder selten das übergeordnete Element) muss stattdessen _exit aufrufen. Verwenden Sie auch nicht stdio bei dem Kind. Das schreit nur nach Ärger.

Einige Bibliotheken haben ein fflushall(), das Sie vor fork() aufrufen können, das stdio im Kind sicher macht. In diesem speziellen Fall würde es auch exit() sicher machen, aber das trifft im allgemeinen Fall nicht zu.

1365820cookie-checkFunktionieren von fork() in Linux gcc [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy