C ++: Verwenden von cin für einen untergeordneten Prozess

8

Ich schreibe ein C ++ - Programm, das sich früh verzweigt und wo ich std :: cout und std :: cin sowohl im Child- als auch im Parent-Prozess verwende. Aus irgendeinem Grund scheint unter Linux cin nicht im Child-Prozess zu arbeiten; Es wird nie nach Eingaben gefragt. Das Lustige daran ist, dass das gleiche Programm auf Mac funktioniert. Weiß jemand, warum das passiert? Danke.

    
metalhead696 13.01.2017, 18:22
quelle

1 Antwort

6

Was Sie beobachtet haben, ist wegen des fork und exec Modells 1 . Alle Dateideskriptoren werden wie erwartet kopiert, aber die Rangfolge, wenn zwei Prozesse von einem einzelnen Deskriptor lesen, ist undefined 2 . Dass sie Eltern und Kind sind, ist irrelevant, sobald die fork () zurückgegeben wird.

Infolgedessen ist Ihre Situation sogar noch schlimmer, als nur von der Implementierung abhängig zu sein. Sie können zwei verschiedene Ergebnisse auf dem SAME-System erhalten.

Dein Szenario wird als Race Condition bezeichnet. Welche der beiden Programmkopien (Eltern oder Kind) erwirbt in solchen Fällen, welches Zeichen von einer Anzahl von zeitbezogenen Details abhängig ist. Sogar die Ressourcen, die andere Prozesse für Ihr System benötigen, könnten in das beobachtete Verhalten eingreifen.

Leseoperationen sind von Natur aus nicht atomar 2 .
Wenn Sie dasselbe Zeichen aus demselben Stream sowohl von Eltern als auch von Kind auf jedem Betriebssystem lesen können, schützt dieses Betriebssystem nicht ordnungsgemäß vor dieser Race-Bedingung und es handelt sich um ein Kernel-Problem, das als möglicher Fehler gemeldet werden sollte 3 .

Sie können diese funktionale Mehrdeutigkeit mit einem Semaphor oder anderen Methoden für die Synchronisation auflösen. Wenn ein solcher Mechanismus ordnungsgemäß verwendet wurde, um atomare Lesevorgänge zu garantieren, können Sie Thread-Sicherheit (oder Prozesssicherheit in diesem Fall) erreichen, aber Sie haben möglicherweise immer noch nicht, was Sie wollen.

Die klassische Lösung könnte darin bestehen, zu entscheiden, welchen der beiden Prozesse Sie std :: cin lesen und std :: cin im anderen Prozess schließen wollen. Der Standardmechanismus, um dies zu tun, besteht darin, die Return-Integer aus dem Fork-Aufruf zu testen. If (fork () == 0) dann bist du im Kind. (Beispiele finden Sie in der Dokumentation zu fork ().)

Wenn Sie den Wert in beiden Prozessen benötigen, können Sie pipe () und dup2 () vor dem fork und den korrekten close () in jedem Prozess verwenden, um eine Kopie der Zeichen vom primären auf den sekundären Reader zu streamen. Dies ist das Proxy-Entwurfsmuster. Wenn verschiedene Nachrichtentypen von verschiedenen Prozessen verarbeitet werden sollen, möchten Sie möglicherweise auch das Designmuster der Verantwortungskette implementieren.

Es ist interessant festzustellen, dass die Ausgabedateideskriptoren, die von std :: cout und std :: cerr umschlossen sind, keine Race-Condition-Risiken haben und Sie die Ausgabe von parent und child 4 mischen können.

.......

[1] POSIX-Standards, die auf das frühe UNIX zurückgehen, so weit zurück wie die PDP11.

[2] Handbuch für die manuelle Suche nach Vorabruf- und Lesezuständen "Die Standardentwickler erwogen, Atomizitätsanforderungen zu einer Pipe oder einem FIFO hinzuzufügen, erkannten diese aber Aufgrund der Beschaffenheit von Pipes und FIFOs kann keine Atomizität von Lesevorgängen von {PIPE_BUF} oder anderen Größen garantiert werden, die der Portabilität von Anwendungen zuträglich wären. "

[3] Zusammenhängende Lesevorgänge derselben Nachrichtenbytes verletzen wahrscheinlich die Standardkriterien. Es sollte ein System-Semaphor oder ein anderer kritischer Code-Schutz um die Erfassung eines Bytes oder eines Zeichens aus einem Eingabestrom bestehen, so dass das gelesene Byte oder Zeichen verworfen wird, bevor es erneut gelesen werden kann.

[4] Das Schreiben in den gleichen Stream von Eltern und Kind, wenn die Nachricht die POSIX-Garantie für diese niedrigstufigen Schreibvorgänge von & lt; = 512 Bytes überschreitet, wird atomar in den Ausgabestrom eingegeben (laut Linux "man 7 Pfeife "). Um die Chronologie mit mehreren Writern zu verwalten, müssten die C-Funktionen auf höherer Ebene oder C ++ - Methoden nach jedem Schreibvorgang geleert werden. Es ist jedoch völlig sicher, mehrere Writer zu haben, wenn bekannt ist, dass die Nachrichten innerhalb des Limits liegen und nur Low-Level-write () - Operationen ausgeführt werden.

    
FauChristian 20.01.2017, 20:56
quelle

Tags und Links