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:
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.
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.
%Vor%schließen Sie / asm-x86 / atomic_64.h
ein
64-Bit-Version:
%Vor%Wir haben Code, der Ihrer Problembeschreibung ähnelt. Wir verwenden eine Speicherabbilddatei, Offsets und Dateisperren. Wir haben keine Alternative gefunden.
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).
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.
Tags und Links multithreading race-condition shared-memory atomic mmap