
Das folgende C-Programm soll den Race Condition zwischen Kind- und Elternprozessen veranschaulichen:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
fork();
printf("\n 1234567890 \n");
return 0;
}
Wenn meine Freunde es ausführen (amUbuntu), erhalten sie die erwartete Ausgabe, die durcheinander ist 1234567890s
Ein Beispiel :12312345645678907890
Aber wenn ich das gleiche Programm auf meinemArch Linux, es gibt nie eine solche Ausgabe. Es kommt immer eins nach dem anderen.
1234567890
1234567890
Ich mag esArch Linuxist irgendwie die Vermeidung von Race Condition, Aber ich möchtedeaktivierenirgendwelche solchen Funktionen und möchte die Ausgabe wie die meines Freundes erhalten.
Antwort1
Der Aufruf würde einen oder mehrere Systemaufrufe printf
ausführen und die Reihenfolge, in der sie verarbeitet werden, wäre die tatsächliche Reihenfolge der Ausgabe. Einer oder mehrere, da dies von der Pufferung innerhalb der C-Bibliothek abhängt. Bei zeilengepufferter Ausgabe (die an das Terminal geht) würden Sie wahrscheinlich zwei Aufrufe erhalten, einen für die anfängliche neue Zeile und einen weiteren für den Rest.write(2)
write
write(1, "\n", 1);
write(1, " 1234567890 \n", 13);
Es ist möglich, dass zwischen den Aufrufen ein anderer Prozess eingeplant wird, der zuerst die beiden leeren Zeilen und dann die Zeilen mit den Ziffern ausgibt. Da jedoch nicht viele Verarbeitungsvorgänge stattfinden, ist dies auf einem unbelasteten System unwahrscheinlich.
Beachten Sie, dass beide Prozesse genau dasselbe drucken. Es spielt also keine Rolle, welcher zuerst ausgeführt wird, solange keiner den anderen unterbricht.
Wenn die Ausgabe in eine Datei oder Pipe geht, wird sie standardmäßig vollständig gepuffert, sodass Sie wahrscheinlich nur einen write
Aufruf (pro Prozess) erhalten und keine Möglichkeit einer gemischten Ausgabe besteht.
Ihr Beispiel mit vermischten Ziffern wäre möglich, wenn die Ziffern einzeln mit einzelnen Systemaufrufen ausgegeben würden. Es ist schwer zu verstehen, warum eine sinnvolle Bibliotheksimplementierung dies tun würde, wenn eine statische Zeichenfolge gedruckt wird, deren Länge bekannt ist. Bei mehr Schreibvorgängen in einer Schleife wäre eine vermischte Ausgabe wahrscheinlicher:
Etwas wie das:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int i;
setbuf(stdout, NULL); /* explicitly unbuffered */
int x = fork();
for (i = 0 ; i < 500 ; i++) {
printf("%d", !!x);
}
if (x) {
wait(NULL);
printf("\n");
}
return 0;
}
Gibt mir eine Ausgabe wie unten. Das ist meistens so, aber nicht immer. Es liegt am System, zu entscheiden, wie die Prozesse geplant werden. Die Unvorhersehbarkeit ist der Grund, warum wir normalerweise versuchen, Race Conditions zu vermeiden.
111111111111111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111111111111111111
111100000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000010000001001100
110000000011001100110011000000010011001100110000000100110011001100000001001
100110011000000010011001100110000000100110011001100000001001100110011000000
...
Antwort2
Ich vermute, dass der fork()
Systemaufruf entweder den übergeordneten oder den untergeordneten Prozess lange genug aufhält, um dem anderen Prozess Zeit zu geben, den Aufruf zu beenden printf()
und den String in der Ausgabe erscheinen zu lassen, bevor er überhaupt zu seinem eigenen gelangt printf()
.
Die Ausgabe einer großen Anzahl von Zeichenfolgen in einer Schleife führt wahrscheinlich zu der von Ihnen beschriebenen vermischten Ausgabe, wenn sowohl der übergeordnete als auch der untergeordnete Prozess Zeit haben, die Schleifen gleichzeitig auszuführen.
Um dies zu „beheben“, müsste wahrscheinlich der fork()
Systemaufruf oder die daran beteiligten Komponenten im Kernel neu geschrieben werden.