Ist es möglich, Zeiger im gemeinsam genutzten Speicher zu speichern, ohne Offsets?
-
21-09-2019 - |
Frage
Bei gemeinsam benutzten Speicher verwendet wird, kann jeder Prozeß den gemeinsamen Bereich in einen anderen Bereich des jeweiligen Adreßraum mmap. Dies bedeutet, dass, wenn Zeiger innerhalb des gemeinsam genutzten Speicherbereich, müssen Sie speichern sie sie als Offsets von Beginn der gemeinsamen Region . Leider verwendet verkompliziert von atomaren Befehlen (zB wenn Sie einen sperrt freien Algorithmus zu schreiben sind versucht, ). Zum Beispiel, sagen Sie eine Reihe von Referenz gezählt Knoten im gemeinsam genutzten Speicher haben, von einem einzigen Schreiber erstellt. Der Schreiber periodisch atomisch aktualisiert einen Zeiger ‚p‘ zu Punkt zu einem gültigen Knoten mit positiven Referenzzählern. Leser wollen ‚p‘ atomar schreiben, weil es an den Anfang eines Knotenpunkte (a struct), deren erstes Element ein Referenzzählwert. Da p auf einen gültigen Knoten zeigt immer, ist die Verweiszähler erhöht wird sicher und macht es sicher zu dereferenzieren ‚p‘ und auf andere Mitglieder. Doch das alles funktioniert nur, wenn alles in demselben Adressraum ist. Wenn die Knoten und die ‚p‘ Zeiger im gemeinsam genutzten Speicher gespeichert sind, dann leiden an den Kunden eine Race-Bedingung:
- x = Lese p
- y = x + Offset
- Increment refcount bei y
Während des Schritts 2 kann p ändern und x kann nicht mehr auf einen gültigen Knoten. Die Problemumgehung nur kann ich mir vorstellen irgendwie alle Prozesse zu zwingen, sich zu einigen, wo die gemeinsam genutzten Speicher auf der Karte, so dass echte Zeiger anstatt Offsets können in der mmap'd Bereich gespeichert werden. Gibt es eine Möglichkeit, das zu tun? Ich sehe in der mmap Dokumentation MAP_FIXED, aber ich weiß nicht, wie ich konnte eine Adresse auswählen, die sicher sein würde.
Edit: Mit Inline-Assembly und der ‚Sperre‘ Präfix auf x86 vielleicht ist es möglich, einen „Zuwachs ptr X mit Y-Offset von Wert Z“ zu bauen? Equivalent-Optionen auf andere Architekturen? Haben nicht viel Assembler geschrieben, weiß nicht, ob die benötigten Anweisungen vorhanden sind.
Lösung
Auf niedrigem Niveau die x86-Atom inctruction tun können alle dieser Baum Schritte auf einmal:
- x = Lese p
- y = x + Offset Increment
- refcount bei y
// mov edi, Destination mov edx, DataOffset mov ecx, NewData @Repeat: mov eax, [edi + edx] //load OldData //Here you can also increment eax and save to [edi + edx] lock cmpxchg dword ptr [edi + edx], ecx jnz @Repeat //
Andere Tipps
Dies ist trivial auf einem UNIX-System; benutzen Sie einfach die Shared-Memory-Funktionen:
shgmet, shmat, shmctl, shmdt
void * shmat (int shmid, const void * shmaddr, int shmflg);
shmat () legt den gemeinsam genutzten Speicher Segment nach shmid auf die identifizierten Adressraum des aufrufenden Prozesses. Die Befestigung Adresse ist spezifiziert durch shmaddr mit einem der folgenden Kriterien:
Wenn shmaddr NULL ist, wählt das System eine geeignete (nicht verwendete) Adresse, bei der das Segment zu befestigen.
Sie Ihre eigene Adresse angeben hier; z.B. 0x20000000000
Wenn Sie shmget () mit dem gleichen Schlüssel und Größe in jedem Prozess, werden Sie das gleiche Shared-Memory-Segment erhalten. Wenn Sie () an der gleichen Adresse shmat, werden die virtuellen Adressen die gleiche in allen Prozessen sein. Der Kernel kümmert sich nicht darum, was Adressbereich Sie verwenden, solange es nicht Konflikt mit, wo es normalerweise ordnet Dinge. (Wenn Sie die Adresse auslassen, können Sie die allgemeine Region sehen, dass es mag Dinge setzen;. Überprüfen Sie auch Adressen auf dem Stapel und kehrte von malloc () / new [])
Unter Linux, stellen Sie sicher, dass root-Sets SHMMAX in / proc / sys / kernel / shmmax auf eine ausreichend große Anzahl gemeinsam genutzten Speichersegmente aufzunehmen (die Standardeinstellung ist 32 MB).
Wie für atomare Operationen, können Sie sie alle von der Quelle Linux-Kernel erhalten, zum Beispiel
include / asm-x86 / atomic_64.h
/*
* Make sure gcc doesn't try to be clever and move things around
* on us. We need to use _exactly_ the address the user gave us,
* not some alias that contains the same information.
*/
typedef struct {
int counter;
} atomic_t;
/**
* atomic_read - read atomic variable
* @v: pointer of type atomic_t
*
* Atomically reads the value of @v.
*/
#define atomic_read(v) ((v)->counter)
/**
* atomic_set - set atomic variable
* @v: pointer of type atomic_t
* @i: required value
*
* Atomically sets the value of @v to @i.
*/
#define atomic_set(v, i) (((v)->counter) = (i))
/**
* atomic_add - add integer to atomic variable
* @i: integer value to add
* @v: pointer of type atomic_t
*
* Atomically adds @i to @v.
*/
static inline void atomic_add(int i, atomic_t *v)
{
asm volatile(LOCK_PREFIX "addl %1,%0"
: "=m" (v->counter)
: "ir" (i), "m" (v->counter));
}
64-Bit-Version:
typedef struct {
long counter;
} atomic64_t;
/**
* atomic64_add - add integer to atomic64 variable
* @i: integer value to add
* @v: pointer to type atomic64_t
*
* Atomically adds @i to @v.
*/
static inline void atomic64_add(long i, atomic64_t *v)
{
asm volatile(LOCK_PREFIX "addq %1,%0"
: "=m" (v->counter)
: "er" (i), "m" (v->counter));
}
Wir Code haben, der auf der Problembeschreibung ähnlich ist. Wir verwenden eine Speicherbild-Datei, Versätze und Dateisperren. Wir haben gefunden, keine Alternative.
Sie sollten keine Angst haben, eine Adresse nach dem Zufallsprinzip zu machen, da der Kernel nur Adressen ablehnt es nicht wie (diejenigen, die im Widerspruch). Siehe meine shmat()
Antwort oben, mit 0x20000000000
Mit mmap:
void * mmap (void * addr, size_t Länge, int Prot, int flags, int fd, off_t Offset);
Wenn Adr nicht NULL, dann wird der Kernel nimmt es als Hinweis darüber, wo zu legen die Abbildung; auf Linux, die Mapping wird auf der nächsten erstellt werden höhere Seitengrenze. Die Adresse Die neue Zuordnung wird als die zurück Ergebnis des Aufrufs.
Das Argument flags legt fest, ob Updates für die Abbildung sind sichtbar andere Prozesse Abbilden der gleichen Region und ob Updates erfolgt durch auf den darunter liegenden Datei. Dieses Verhalten wird bestimmt durch einschließlich genau eines der folgenden Werte in Fahnen:
MAP_SHARED diese Zuordnung Teile. Updates für die Abbildung sind sichtbar andere Prozesse, die diese Karte Datei, und an die durch durch zugrunde liegende Datei. Die Datei kann nicht tatsächlich bis msync aktualisiert werden (2) oder munmap () aufgerufen wird.
FEHLER
EINVAL Wir haben nicht wie Adr, Länge oder Offset (beispielsweise sind sie zu groß ist, oder nicht auf einer Seitengrenze ausgerichtet).
Das Hinzufügen der auf den Zeiger versetzen das Potenzial für ein Rennen nicht schaffen, es ist bereits vorhanden. Da zumindest weder ARM noch x86 atomar einen Zeiger lesen kann dann Zugriff auf den Speicher bezieht es sich auf Sie den Mauszeiger Zugriff mit einer Sperre unabhängig von schützen müssen, ob Sie einen Offset hinzufügen.