Übertragung dynamischer Datenstrukturen zwischen Prozessen
-
Hallo Forum,
ich sucher momentan krampfhaft nach einer Möglichkeit dynamische Datenstrukturen mittels IPC zwischen Prozessen auszutauschen.
Ich hab 2 Prozesse..beide haben ihren eigenen Quellcode (das Kind wird durch exec-Befehl vom Vater aufgerufen)..nun habe ich eine dynamische Datenstruktur..z.B. struct test{char *bla}; .. Problem ist jetzt, dass ich für 'bla' eigenen Speicher allokieren muss, welcher außerhalb des Speicherbereichs vom struct selbst liegt. Versende ich nun die struct mit Hilfe von Pipes oder Message Queues, so wird ja nur die Adresse von 'bla' versendet, die dem anderen Prozess nichts nützt, da er einen eigenen Speicherbereich hat.
Gibts da eine Möglichkeit, um dieses Problem zu lösen? Geht das vielleicht mit Shared Memory oder m.H. von Sockets? Oder muss man immer statisch Datenstrukturen definieren..nur dann kann man ja keine verkettete Liste so einfach übertragen, wie macht man das denn?MfG inge85
-
Prinzipiell könnte man das mit shared_memory lösen, aber dann ist Interprozesskommunikation sowieso ziemlich witzlos. Das übliche Vorgehen wäre, dass du die Elemente verschickst, nicht die Datenstruktur. Das muss auch nicht unbedingt einzeln geschehen, du kannst auch vorher eine Mengenangabe versenden, damit der andere Prozess einen ausreichend großen Empfangspuffer bereithalten kann.
-
Mhh..also die Elemente einzeln versenden finde ich ziemlich blöd. Da muss ich ja die gesamte Datenstruktur zerpflücken und auf der anderen Seite wieder zusammenbauen..das ist ja ganz schöne Programmieraufwand.
Meine Idee ist jetzt Folgende..TROMMELWIRBEL..ich nutze Shared Memory + Pipes.^^
Und zwar definiere ich mir ein passendes Stück Speicher für meine aktuelle Datenstruktur..pack sie da rein..und übermittel dann die Anfangsadresse des Shared-Memory-Bereichs mittels einer Pipe an den anderen Prozess.
Was haltet Ihr von der Idee?^^
-
Viel geschickter wäre es einfach den shared memory key zu übertragen.
Die Anfangsadresse bringt dir wenig.
-
Wenn ich richtig rauslese, sind die Prozesse verwand (Parent child), der TE kann also die unnamed IPC-Objecte einfach vererben lassen (vor dem fork anlegen, nach dem fork kennt das parent und der child dann).
Prinzipiell könnte man das mit shared_memory lösen, aber dann ist Interprozesskommunikation sowieso ziemlich witzlos.
BTW, ich habe Shared memory als die einfachste Form der IPC betrachtet ^^
@TE
zuerst wuerd ich versuchen, deine Strukturen so bauen, dass sie am Stueck im Speicher liegen. Macht man prinzipiell oft, wenn man sowieso weiss das die serialisiert werden muessen, bzw die serialisation (kommunikation) ein wesentlicher bestandteil der verwendung ist.
Bevor ich die Kommunikation zerstueckle, baue ich lieber Klassen, die in der Datenhaltung im Speicher aufwendiger zu programmieren sind.Ab und an kommt man aber damit mit der Performance ned hin ...
entweder 2 versionen einer Klasse fuer logisch die selben Objecte, einmal performant, und einmal einfach zu serialisieren, und vorm kommunizieren fix umwandeln ...Oder halt ein Komplexeres Kommunikationsprotokoll verwenden.
Das komplexere Modell ist aber meist ein Alptraum, es sei denn man liebt es Zustands-Automaten zu schreibenCiao ...
-
@linux_c89: ja ich habe vor den shared key zu übertragen! allerdings fällt mir gerade auf, dass es doch nicht soo leicht ist meine dynamische datenstruktur einfach in den shared memory bereich zu legen..ich allokiere ja speicher für die einzelnen komponenten der datenstruktur..und diese liegen dann ja nicht am stück im speicher..wie krieg ich das denn hin?^^ (komme eher aus der java-welt :-P)
@RHBaum: ehm..bitte nicht steinigen, aber was ist TE!?^^ klingt ja interessant mit der vererbung. aber nochmal kurz zum programmaufbau und ein paar randregeln: ich habe ein hauptprogramm in c++..hier mache ich ein fork, wobei der vater in einer dauerschleife auf nachrichten vom sohn wartet, welcher wiederum mittels exec ein anderes programm aufruft und darin weiterläuft. dieser andere quellcode ist in c (MUSS, geht nicht anders!!). nun läuft der sohn hier in einer dauerschleife und bekommt ab und zu eine nachricht übersendet (über netzwerk)..diese nachricht enthält die datenstruktur, welche unterschiedliche ausfallen kann..da sie halt dynamisch ist (ineinander geschachtelte verkettete listen). aufgabe ist es nun, dass der C-prozess seinem C++-vater diese datenstruktur überträgt, damit der vater damit "was tolles" machen kann.
ich hoffe mein problem wird nun ein wenig klarer..
-
Das einzige was mir da einfallen würde wäre die Gesamtgröße deiner Struktur festzustellen (nach dem empfangen, da sie ja dynamisch ist), den shared memory in der passenden Größe allozieren, Größe und key an den Vater zu übertragen und dann die Struktur in den shared memory reinzukopieren (unter Auflösung aller Zeiger usw).
Da das nicht mehr wirklich simpel ist wäre vielleicht eine pipe zwischen Vater und Kind (Kind hat Schreibzugriff, Vater Lesezugriff) durch das man die Struktur Stück für Stück (unter Auflösung der Zeiger) durchschickt simpler.
-
@linux_c89: genau deine beschriebene variante ist mir halt zu "unelegant" .. sie ist die naheliegenste..aber verdammt, das muss doch besser gehen!!^^
-
Schreibs doch einfach mal so und änder es wenn dir was besseres einfällt
-
@inge
TE = Thread-Ersteller - in diesem Falle also DU !dieser andere quellcode ist in c (MUSS, geht nicht anders!!)
Hehe, vererbung war hier nicht im Sinne von OOP gemeint, sondern Prozesstechnisch.
Ganz einfach. unter Unix sind 2 Prozesse vererbt, wenn sie eine Parent-Child beziehung haben, Aka du fork benutzt. Hat nix mit der Programmiersprache zu tun.
Der Child "erbt" waehrend dem fork aufruf alle Systemressoursen vom parent, inklusive Handles auf geoffnete dateien und IPC Komponenten (Shared Memory z.b.)
Heisst in der Praxis braucht man sich da das geschmarre mit dem "Named" globalen Systemobjecten antun, sondern kann annonyme unbenamste verwenden.Nun die Schlechte Nachricht:
Ein "exec" bricht diese Verrebung auf, klar. Dein ueberlagerter prozess kennt nix von seinem Vorlaeufer Du musst also trotzdem die benamsten Teile verwenden.
In deinem Falle also nur intressante Info am Rande, weil hilft Dir nicht weiterDa du aber scheinbar den Parent und den Child in der Hand hasst, weil du willst / musst beide seiten der Communikation implementieren, bleibt die Spezielle Frage sowieso : Warum das Exec ??? muss dein C-Code programm unbedingt nen binary sein ? Warum ? Ne Möglichkeit waer statt nem binary ne lib (.so) zu bauen. kann man auch mit C ...
Und warum um Himmelswillen bist du an C gebunden ?Da das nicht mehr wirklich simpel ist wäre vielleicht eine pipe zwischen Vater und Kind
Shared memory sollte man nur verwenden, wenn man wirklich "wahlfrei"(also mal an die stelle mal an die Stelle) auf dem Speicherbereich rumrutschen muss.
Reduziert sich die verwendung auf, der Parent schreibt rein, signalisiert dem Child das er lesen kann, der child liest es aus .... sollt man sich das ganze sparen und gleich ne pipe nehmen, weil Die implementiert genau dies !Und laut definition von inge ist genau dies der Fall:
aufgabe ist es nun, dass der C-prozess seinem C++-vater diese datenstruktur überträgt, damit der vater damit "was tolles" machen kann.
Das klingt definitiv nach Pipe !
einzigstes Problem was ich sehe, ist die Datenstrukturen "flachzuklopfen", so dass sie durch die Pipe passen !
Da gibts aber mehrere Aspekte die zu beachten sind z.b.
Performance, Operabilitaet (wie toll muss man den Inhalt debugen koennen), etc.
Eigentlich ist es nen Protokoll was man dazu spezifiziert ...
Also:
- wie Performant muss es sein ?
- wie fest ist die Parent-Child Bindung ? Ist es denkbar, das es andere parents (Andere Version / Projecte) und andere Chields geben kann, die untzereinander austauschbar sind ?
- will man den Datenaustausch monitoren ?Ciao ...
-
Hallo zusammen,
interessantes Thema was ihr da habt.
Also normalerweise nimmt man wie schon gesagt eine pipe.
Klassisch muss man dann seine Objekte serialisieren (mehr oder weniger elegant kopieren).Wenn man sich die Kopieroperation (zur Laufzeit) 'sparen' möchte,
wäre shared memory theoretisch eine Lösung.Der Shared Memory Ansatz macht gegenüber einer pipe nur dann Sinn,
wenn die Datenhaltung, aller relevanten Objekte, schon serialisiert ist.
Sonst hätte man die selben Kopien, nur an andere Stelle.Das bedeutet man muss die Objecte schon serialisiert aufbauen.
Hinweis: Der Shared memory ist zwar der gleiche reale Speicher,
aber er wird an unterschiedlichen virtuellen Adressen in den Prozessen eingeblendet. Letztendlich muss ich den shared memory wie ein file betrachten.
Das heißt, dass ich z.B. keine pointer in den shared memory legen darf.
Natürlich kann ich innerhalb des shared memory referenzieren,
aber nur mit Offsets zum Start Punkt des shared memory.Ich hatte so etwas Ähnliches selbst schon mal programmiert,
jedoch nicht mit shared memory, ich hatte ein eigenes,
memroy handling aus bestimmten Gründen dazwischen geschaltet.Was hilft ist sich zunächst eine File Struktur zu überlegen,
welche alle Datenobjekte und erforderliche Indexe enthält.
Hierbei muss man unbedingt das Allinment einhalten,
sonst kann es fiese Überraschungen geben.Schwierig wird es wenn viele verschiedene Object Typen existieren.
Hier macht es Sinn entweder eine eigene Type ID mit zu serialisieren
oder pro Object Type einen eigenen shared memory zu halten.
Beides hilft die dunkle Seite der Macht zurück zu halten.Wenn all diese Objects auch noch in nicht vorhersehbarer Menge existieren können, wird es richtig anspruchsvoll.
shared memory kann man, so glaube ich auch reallozieren.
Hier kann es wieder Sinn machen Object Type einen eigenen shared memory zu halten.Im nächsten Schritt benötigt man dann nur noch Funktionen,
welche die Objekte direkt im shared memory erstellen und
Zugriffs Zeiger darauf zurückgeben.
Weitere Funktionen (oder #defines) um die Daten des Objects zu schreiben
Weitere Funktionen (oder #defines) um die Daten des Objects zu lesenDie pipe benötigt man eventuell immer noch,
zum Signalisieren das neue Daten da sind.Monitorring geht auch,
man kann den shared memory als Zwitter (file und memory) öffnen.Diesen Teil würde ich nur in C schreiben.
Und ich würde davon ausgehen dass mir das Programm erst mal eine Weile
hässlich crashed, bevor es dann rasend schnell läuft.@Inge
Wenn du keine speziellen Anforderungen nennst,
außer "das muss doch besser gehen"
empfehle ich dir statische Strukturen und eine pipe zu verwenden,
bzw. noch besser auf die zwei Prozesse zu verzichten und
alles in einem Prozess zu machen.Gruß Frank
-
Boa erstmal vielen Dank für die rege Diskussion!!!
Nun zu meinem Stand..alles, was ich hier gelesen habe, habe ich mir auch in den letzten Tagen selbst erarbeitet (ich hätte doch mal früher hier reingucken sollen^^, wobei ichs natürlich nun viel besser verstehe, da ich es mir selbst erarbeitet habe :D)..ich war schon kurz davor eine eigene malloc()-Funktion zu schreiben, die mir Speicher innerhalb des Shared-Memory allokiert. Dies wäre eine super Lösung finde ich.^^Aufgrund von Zeitmangel im Rahmen meines Projekts musste ich allerdings darauf verzichten und die statische Methode mittels Pipe realisieren. Sollte ich nochmal Zeit haben, werde ich meine dynamische Variante mittels Shared-Memory + Pipe (zur Signalisierung) realisieren und euch auf jeden Fall Bescheid geben.
Zu den 1000 Fragen..ich kann leider nicht alle genau beantworten, da es meist projektinterne Gründe sind. Ihr müsst mir einfach glauben, dass ich C für den einen Prozess verwenden MUSS..und dass dieser Prozess mittels exec aufgerufen werden MUSS. Das liegt daran, dass der Quellcode durch eine Open-Source-Lib erstellt wird, welchr dann unter für uns ungünstigen Lizenzbedingungen steht. Der Quellcode dieses Prozesses muss veröffentlicht werden und außerdem alles, was dazu gelinkt wird. Rufen wir den Prozess nun mittels exec auf, so können wir unsere komplette Logik (die wir nicht veröffentlichen wollen^^) auslagern und geheim halten. Ich hoffe das beantwortet einigermaßen die Fragen zu diesem Thema.
Zur Frage wie performant es sein muss: SEHR! Der exec-Prozess wird einen Haufen Nachrichten Nachrichten in sehr kurzer Zeit kriegen, die er alle an den Vater weitergeben und durch ihn verarbeiten lassen soll. Um so länger das Weiterreichen + Verarbeitun dauert, um so mehr Nachrichten können nicht verarbeitet und müssen wieder verworfen werden. Wieviele Nachrichten es genau werden pro Zeit kann ich noch nicht genau sagen..ist leider auch vom späteren Anwendungsszenario abhängig. Einigen wir uns einfach darauf, dass es enorm viele sein können und das Weiterreichen dabei maximal schnell sein sollte (so wie eigentlich auch alles andere im Programm^^).
Nun zu den Lösungsideen..im Kern aller Posts steckt letztlich die Aussage, dass ich statische Strukturen verwenden soll.^^ Das ist aber suboptimal..ich verwende halt verkettete Listen, da ich nie wissen kann, wieviele Elemente eine Liste letztlich enthalten wird. Natürlich gibts ein Maximum..aber dann müsste ich auch immer das Maximum an Daten übertragen, was sehr unperformant ist.
Also Fakt ist, dass ich auf dynamische Strukturen nicht verzichten kann. Allerdings wird mit immer mehr klar, dass ich so oder so diese Datenstruktur irgendwie umbauen muss, damit sie übertragbar ist zwischen den Prozessen. Also werde ich so oder so Zeit dafür verlieren. Nun ist die Frage, welche Umwandlung am wenigsten Zeit kostet!? Umbau in statische Strukturen mit anschließender Übertragung mittels Pipe? Oder mittels Shared-Memory (+Pipe zur Signalisierung)? Oder eigene malloc-Funktion schreiben mit der ich meine Datenstruktur neu dynamisch in den Shared-Memory bauen kann?Bin auf jeden Fall ganz schön erschrocken, dass es da noch keine Patentlösung gibt! Ich kann doch nicht der erste Mensch auf Erden sein, der eine verkettete Liste zwischen 2 Prozessen übertragen will?? Da muss sich doch schon längst mal wer hingesetzt haben und "malloc2" oder Ähnliches implementiert haben.^^
-
Hallo Inge:
Da muss sich doch schon längst mal wer hingesetzt haben und "malloc2" oder Ähnliches implementiert haben.
Es gibt sicherlich einige libs, die Serialisierung unterstützen,
vielleicht auch welche wo man einen allocator mit angeben kann
oder sogar einen shared memory allocator aussuchen kann.
Oft wird jedoch klassisch kopiert.
Geh doch mal suchen und prüfe ob es deinen Anforderungen genügt.
Ich würde es selber schreiben, wenn die Notwendigkeit da wäre.
Hinweis:
Die Notwendigkeit war in mehr als 10 Jahren nicht gegeben,
weil die Vorverarbeitung und Nacharbeitung meistens deutlich mehr Performance schluckt.Es ist unter Umständen maximale Performance möglich,
so etwas zu nutzen kann ein Wettbewerbsvorteil sein ...
Ob entsprechende libs frei verfügbar sind?Ein Ansatz über eine Pipe ist oft ähnlich schnell,
und verbraucht weniger Speicher wenn streamed verarbeitet werden kann.
Die Pipe benutzt ja intern shared memory.
Im Grunde ist streamed Verarbeitung sowieso die hohe Kunst,
leider sind die Software Designs zu selten geeignet für solche streamed Ansätze.Gruß Frank