Ist es möglich, Zeiger im Shared Memory zu speichern, ohne Offsets zu verwenden?

8

Bei der Verwendung von gemeinsam genutztem Speicher kann jeder Prozess die gemeinsame Region in einen anderen Bereich seines jeweiligen Adressraums kopieren. Dies bedeutet, dass Sie beim Speichern von Zeigern innerhalb der gemeinsamen Region diese als Offsets speichern müssen des Beginns der gemeinsamen Region. Leider erschwert dies die Verwendung von atomaren Anweisungen (zB wenn Sie versuchen, einen Lock Free-Algorithmus zu schreiben) . Angenommen, Sie haben eine Reihe von Referenzzählknoten im gemeinsam genutzten Speicher, die von einem einzelnen Schreiber erstellt wurden. Der Schreiber aktualisiert periodisch automatisch einen Zeiger "p", um auf einen gültigen Knoten mit einer positiven Referenzzahl zu zeigen. Leser möchten atomar auf 'p' schreiben, da sie auf den Anfang eines Knotens (einer Struktur) zeigt, dessen erstes Element eine Referenzzahl ist. Da p immer auf einen gültigen Knoten zeigt, ist das Erhöhen der Ref-Zählung sicher und macht die Dereferenzierung von "p" und den Zugriff auf andere Member sicher. Dies funktioniert jedoch nur, wenn sich alles im selben Adressraum befindet. Wenn die Knoten und der 'p' -Zeiger im Shared Memory gespeichert sind, leiden Clients unter einer Race-Bedingung:

  1. x = lies p
  2. y = x + Verschiebung
  3. Refcount um y erhöhen

Während Schritt 2 kann sich p ändern und x darf nicht mehr auf einen gültigen Knoten zeigen. Die einzige Problemumgehung, die ich mir vorstellen kann, ist, alle Prozesse dazu zu zwingen, sich darauf zu einigen, wo der geteilte Speicher zugeordnet werden soll, so dass reale Zeiger statt Offsets in der mmap-Region gespeichert werden können. Gibt es eine Möglichkeit, das zu tun? Ich sehe MAP_FIXED in der mmap-Dokumentation, aber ich weiß nicht, wie ich eine Adresse auswählen könnte, die sicher wäre.

Edit: Mit Inline Assembly und dem Präfix 'lock' auf x86 ist es möglich, ein "increment ptr X mit Offset Y um den Wert Z" zu erstellen? Gleichwertige Optionen auf anderen Architekturen? Habe nicht viel Assembly geschrieben, weiß nicht, ob die benötigten Anweisungen existieren.

    
Joseph Garvin 22.03.2010, 03:39
quelle

5 Antworten

3

Auf niedriger Ebene kann die x86-Atomkrümmung alle diese Baumschritte gleichzeitig ausführen:

  1. x = lies p
  2. y = x + Offset Inkrement
  3. refcount bei y
%Vor%
    
GJ. 22.03.2010, 10:44
quelle
3

Dies ist auf einem UNIX-System trivial; benutze einfach die Shared Memory Funktionen:

shgmet, shmat, shmctl, shmdt

void * shmat (int shmid, const void * shmaddr, int shmflg);

  

shmat () verbindet den gemeinsamen Speicher   Segment identifiziert durch shmid zum   Adressraum des aufrufenden Prozesses.   Die Anbringungsadresse wird mit angegeben   Shmaddr mit einem der folgenden   Kriterien:

     

Wenn shmaddr NULL ist, wählt das System   eine geeignete (unbenutzte) Adresse, an der   um das Segment anzuhängen.

Geben Sie hier einfach Ihre eigene Adresse an; z.B. 0x20000000000

Wenn Sie shmget () mit demselben Schlüssel und derselben Größe in jedem Prozess verwenden, erhalten Sie dasselbe Shared-Memory-Segment. Wenn Sie shmat () an der gleichen Adresse senden, sind die virtuellen Adressen in allen Prozessen identisch. Dem Kernel ist es egal, welchen Adressbereich Sie verwenden, solange er nicht in Konflikt mit den normalerweise zugewiesenen Adressen steht. (Wenn Sie die Adresse weglassen, können Sie die allgemeine Region sehen, in die sie gerne Dinge legt; überprüfen Sie auch die Adressen auf dem Stapel und geben Sie sie von malloc () / new [] zurück.)

Stellen Sie unter Linux sicher, dass root SHMMAX in / proc / sys / kernel / shmax auf eine Größe einstellt, die groß genug ist, um Ihre Shared Memory-Segmente aufzunehmen (Standard ist 32 MB).

Wie für atomare Operationen können Sie sie alle von der Linux-Kernel-Quelle, z.

  

schließen Sie / asm-x86 / atomic_64.h

ein
%Vor%

64-Bit-Version:

%Vor%     
shm skywalker 20.04.2010 01:48
quelle
2

Wir haben Code, der Ihrer Problembeschreibung ähnelt. Wir verwenden eine Speicherabbilddatei, Offsets und Dateisperren. Wir haben keine Alternative gefunden.

    
Steve Emmerson 22.03.2010 03:59
quelle
2

Sie sollten keine Angst haben, eine zufällige Adresse zu finden, weil der Kernel nur Adressen ablehnt, die er nicht mag (Konflikte, die ihm widersprechen). Siehe oben meine shmat() Antwort mit 0x20000000000

Mit mmap:

  

void * mmap (void * adr, size_t length, int prot, int-Flags, int fd, off_t-Offset);

     

Wenn addr nicht NULL ist, dann der Kernel   Nimmt es als Hinweis darauf, wohin   Platziere das Mapping; auf Linux, der   Mapping wird beim nächsten erstellt   höhere Seitengrenze Die Adresse von   Das neue Mapping wird als das zurückgegeben   Ergebnis des Anrufs.

     

Das Argument flags bestimmt, ob   Updates für das Mapping sind sichtbar für   andere Prozesse, die dasselbe abbilden   Region, und ob Updates sind   bis zum Basiswert durchgeführt   Datei. Dieses Verhalten wird bestimmt durch   einschließlich genau einer der folgenden   Werte in Flags:

     

MAP_SHARED Teilen Sie dieses Mapping.   Aktualisierungen für das Mapping sind sichtbar für   andere Prozesse, die dies abbilden   Datei, und werden durch die   zugrunde liegende Datei. Die Datei darf nicht   eigentlich bis msync (2) oder aktualisiert werden   munmap () wird aufgerufen.

     

FEHLER

     

EINVAL Wir mögen nicht addr, length oder   Offset (z. B. sie sind zu groß, oder   nicht an einer Seitengrenze ausgerichtet).

    
shm skywalker 20.04.2010 02:00
quelle
1

Das Hinzufügen des Versatzes zum Zeiger erzeugt nicht das Potenzial für eine Rasse, es existiert bereits. Da weder ARM noch x86 nur einen Zeiger lesen können und dann auf den Speicher zugreifen, auf den er verweist, müssen Sie den Zeigerzugriff mit einer Sperre schützen, unabhängig davon, ob Sie einen Offset hinzufügen.

    
Timothy Baldwin 18.08.2016 16:23
quelle