Der Kontext ist Inter-Process-Communication, bei der ein Prozess (“Server”) Strukturen mit fester Größe an viele lauschende Prozesse (“Clients”) senden muss, die auf demselben Computer ausgeführt werden.
Ich fühle mich sehr wohl dabei, dies in der Socket-Programmierung zu tun. Um die Kommunikation zwischen dem Server und den Clients schneller zu machen und die Anzahl der Kopien zu reduzieren, möchte ich die Verwendung von Shared Memory (shm) oder mmaps ausprobieren.
Das Betriebssystem ist RHEL 64bit.
Da ich ein Neuling bin, schlagen Sie bitte vor, welches ich verwenden soll. Ich würde mich freuen, wenn mich jemand auf ein Buch oder eine Online-Ressource verweisen könnte, um dasselbe zu lernen.
Danke für die Antworten. Ich wollte hinzufügen, dass der Server ( Marktdatenserver ) normalerweise Multicast-Daten empfängt, was dazu führt, dass er etwa 200.000 Strukturen pro Sekunde an die „Clients“ „sendet“, wobei jede Struktur ungefähr 100 Bytes groß ist. Übertrifft die Implementierung von shm_open/mmap Sockets nur für große Datenblöcke oder auch für eine große Menge kleiner Strukturen?
Jens Gustedt
Ich würde verwenden mmap zusammen mit shm_open gemeinsam genutzten Speicher in den virtuellen Adressraum der Prozesse abzubilden. Das ist relativ direkt und sauber:
Sie identifizieren Ihr gemeinsames Speichersegment mit einer Art symbolischem Namen, so etwas wie "/myRegion"
mit shm_open Sie öffnen einen Dateideskriptor für diese Region
mit ftruncate Sie vergrößern das Segment auf die gewünschte Größe
mit mmap Sie ordnen es Ihrem Adressraum zu
Das shmat und Co-Schnittstellen haben (zumindest historisch) den Nachteil, dass sie eine Beschränkung in der maximal abbildbaren Speichermenge haben können.
Dann werden alle POSIX-Thread-Synchronisationstools (pthread_mutex_t, pthread_cond_t, sem_t, pthread_rwlock_t, …) verfügen über Initialisierungsschnittstellen, die es Ihnen ermöglichen, sie auch in einem gemeinsam genutzten Prozesskontext zu verwenden. Alle modernen Linux-Distributionen unterstützen dies.
Ob dies gegenüber Steckdosen vorzuziehen ist oder nicht? In Bezug auf die Leistung könnte dies einen kleinen Unterschied machen, da Sie die Dinge nicht kopieren müssen. Aber der Hauptpunkt wäre meiner Meinung nach, dass dies konzeptionell etwas einfacher ist, sobald Sie Ihr Segment initialisiert haben. Um auf ein Element zuzugreifen, müssen Sie nur ein gemeinsames Schloss sperren, die Daten lesen und das Schloss dann wieder entsperren.
Wie @R vorschlägt, wenn Sie mehrere Leser haben pthread_rwlock_t wäre wahrscheinlich die beste zu verwendende Sperrstruktur.
Wenn Sie nur an einem Ende lesen, sind Mutexe das falsche Synchronisierungselement. Sie sollten RWlocks verwenden.
– R.. GitHub HÖR AUF, EIS ZU HELFEN
29. Januar 2011 um 13:37 Uhr
Ich habe einmal eine IPC-Bibliothek mit Shared-Memory-Segmenten implementiert; Dadurch konnte ich eine Kopie vermeiden (anstatt Daten aus dem Senderspeicher in den Kernel-Speicherplatz und dann vom Kernel-Speicherplatz in den Empfängerspeicher zu kopieren, konnte ich direkt vom Sender- in den Empfängerspeicher kopieren).
Wie auch immer, die Ergebnisse waren nicht so gut, wie ich erwartet hatte: Die gemeinsame Nutzung eines Speichersegments war ein wirklich teurer Prozess, da das Neuzuordnen von TLB-Einträgen und allem anderen ziemlich teuer ist. Sehen diese Post für weitere Details (ich bin keiner von diesen Typen, bin aber während der Entwicklung meiner Bibliothek auf solche E-Mails gestoßen).
Die Ergebnisse waren nur für wirklich große Nachrichten gut (mehr als ein paar Megabyte). Wenn Sie mit kleinen Puffern arbeiten, sind Unix-Sockets das Optimierteste, was Sie finden können, es sei denn, Sie sind bereit, ein Kernelmodul zu schreiben.
Auf den ersten Blick mag dies recht schwerfällig erscheinen, aber die meisten Betriebssysteme implementieren Loopback-Sockets in einer Zero-Copy-Architektur. Die Seite(n), die dem zugeordnet sind buf Parameter übergeben send wird eine zusätzliche Zuordnung zugewiesen und beim Schreiben als Kopie markiert, so dass, wenn das sendende Programm die darin enthaltenen Daten überschreibt oder die Zuordnung aufhebt, der Inhalt erhalten bleibt.
Danke für die Links. Die Zero-Copy-Referenz war in der Tat hilfreich. Ich konnte nicht herausfinden, wie RHEL6 Local Multicast behandelt (vom Bezugspunkt der Zero-Copy-Architektur). Hätten Sie eine Referenz zu diesem Thema?
– Bescheidener Debugger
30. Januar 2011 um 3:05 Uhr
@HumbleDebugger: RHEL ist nur eine weitere Linux-Distribution, und Linux ist einer dieser Kernel, der Zero Copy in den Socket-Puffer implementiert. Tut mir leid, dass ich so spät geantwortet habe, aber Ihr ursprünglicher Kommentar ist nicht in meinen Benachrichtigungen aufgetaucht, und ich habe ihn erst heute gesehen, als meine Antwort ein anderes Mal positiv bewertet wurde.
– Datenwolf
12. Februar 2013 um 11:33 Uhr
Nachdem ich es öfter in beide Richtungen gemacht habe, als ich zählen möchte, wäre die Verwendung von Sockets in einem neuen Projekt für IPC für mich wie Gandalfs Vorbehalte, die Minen von Moria zu betreten. Man wird das Gefühl einfach nicht los, einem Balrog über den Weg zu laufen. COW ist ein Schwergewicht, wenn Sie häufig auf die Seiten schreiben, weil Sie dann zusätzlich zu der Kopie die TLB ungültig machen müssen, und wie Linus es ausdrückt, “Sie gehören genau in die Kategorie, die scheiße ist”. structs + shmem = einfach und Top-Performance, Sockets + Serialisierung = komplex und langsamer. Ich weiß nicht, warum sich so viele für Letzteres entscheiden.
– Eloff
11. September 2013 um 14:30 Uhr
@Eloff: Weil Robustheit und Integrität in IPC wichtig sind, während einfache Leistung normalerweise Zerbrechlichkeit impliziert, was Sie in IPC vermeiden möchten. Ja, es gibt Anwendungen für SHM und es gibt Situationen, in denen Sie rohe Leistung benötigen. Aber wenn Sie möchten, dass zwei Prozesse kommunizieren, ohne sich gegenseitig auf die Zehen treten zu können (denken Sie an Sandbox-Worker), dann bietet Ihnen ein gut kanalisierter Socket einen klaren Eintrittspfad für neue Daten.
– Datenwolf
11. September 2013 um 15:33 Uhr
Sicher, aber Sie werden am Ende viel mehr Code haben. Eine einfache Shared-Memory-Lösung mit einem einfachen Sperrschema ist leichter zu verstehen und weniger anfällig für Fehler. Aber das ist nur meine Meinung und deine ist offensichtlich anders.
– Eloff
19. September 2013 um 20:53 Uhr
Diomidis Spinellis
Die Auswahl zwischen den POSIX shm_open/mmap Schnittstelle und das ältere System V shmop one wird keinen großen Unterschied machen, denn nach den Systemaufrufen der Initialisierung haben Sie am Ende die gleiche Situation: einen Speicherbereich, der von verschiedenen Prozessen gemeinsam genutzt wird. Wenn Ihr System dies unterstützt, würde ich empfehlen, mitzumachen shm_open/mmapda dies eine besser gestaltete Schnittstelle ist.
Den gemeinsam genutzten Speicherbereich nutzen Sie dann als gemeinsame Tafel, auf der alle Prozesse ihre Daten kritzeln können. Der schwierige Teil besteht darin, die Prozesse zu synchronisieren, die auf diesen Bereich zugreifen. Hier empfehle ich zu vermeiden, sich ein eigenes Synchronisationsschema auszudenken, das teuflisch schwierig und fehleranfällig sein kann. Verwenden Sie stattdessen die vorhandene funktionierende Socket-basierte Implementierung zum Synchronisieren des Zugriffs zwischen Prozessen und verwenden Sie den gemeinsam genutzten Speicher nur zum Übertragen großer Datenmengen zwischen Prozessen. Auch bei diesem Schema benötigen Sie einen zentralen Prozess, um die Zuweisung von Puffern zu koordinieren, daher lohnt sich dieses Schema nur, wenn Sie sehr große Datenmengen zu übertragen haben. Verwenden Sie alternativ eine Synchronisationsbibliothek, z Boost.Interprozess.
11804200cookie-checkShared Memory oder mmap – Linux C/C++ IPCyes