Shared Memory oder mmap – Linux C/C++ IPC

Lesezeit: 6 Minuten

Benutzer-Avatar
Bescheidener Debugger

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?

Benutzer-Avatar
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.

Abgesehen von dem, was bereits vorgeschlagen wurde, möchte ich eine andere Methode anbieten: IPv6 Node/Interface Local Multicast, dh ein Multicast, das auf die Loopback-Schnittstelle beschränkt ist.
http://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xml#ipv6-multicast-addresses-1

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.

Anstatt rohe Strukturen zu übergeben, sollten Sie eine robuste Datenstruktur verwenden. Netzschnüre http://cr.yp.to/proto/netstrings.txt und BSON http://bsonspec.org/ in den Sinn kommen.

  • 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

Benutzer-Avatar
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.

1180420cookie-checkShared Memory oder mmap – Linux C/C++ IPC

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

Privacy policy