Zeiger in Shared Memory



  • Hallo,

    Mich würde interessieren, wo die Fallstricke bei Zeigern in Shared Memory sind.
    Mir ist das noch nicht so ganz klar.

    Gruß Alex



  • //Frage falsch verstanden



  • Ein Problem ist, wenn du in einer Struktur im shared memory einen Zeiger hast und der auf Speicher außerhalb des shared memory zeigt. Auch innerhalb hast du immer noch das Problem, dass der Speicherbereich in einem anderen Prozess an einer anderen Stelle im virtuellen Speicher gemappt sein kann, dort ist der Zeiger also u.U. auch ungültig.
    Angehen kannst du das Problem mit relativer Addressierung.



  • Also ein und die Selbe physikalische Addresse können in zwei verschiedenen Prozessen zwei unterschiedliche Zahlen haben, welche den Speicher repräsentieren ??
    Umgekehrt, wenn zwei Prozesse die gleiche Zahl,welche als Zeiger interprätiert wird dereferenzieren, können sie an zwei unterschiedlichen Speicherstellen raus kommen??

    In wie fern hilft da relative Addressierung ???
    (Das sind zum Beispiel smart pointer habe ich gelesen ???)

    Gruß



  • AlexanderKiebler schrieb:

    Also ein und die Selbe physikalische Addresse können in zwei verschiedenen Prozessen zwei unterschiedliche Zahlen haben, welche den Speicher repräsentieren ??

    Ja.

    AlexanderKiebler schrieb:

    Umgekehrt, wenn zwei Prozesse die gleiche Zahl,welche als Zeiger interprätiert wird dereferenzieren, können sie an zwei unterschiedlichen Speicherstellen raus kommen??

    Ja, sogar mit großer Wahrscheinlichkeit und das ist ja auch der Sinn von virtuellem Speicher.

    AlexanderKiebler schrieb:

    In wie fern hilft da relative Addressierung ???
    (Das sind zum Beispiel smart pointer habe ich gelesen ???)

    Nicht Smartpointer im herkömmlichen Sinn. Anstatt eine absolute Adresse zu speichern, merkst du dir nur: der referenzierte Speicher liegt z.B. 430 Bytes nach mir. Diesen Wert kannst du dann in der Tat in einer Zeigerklasse kapseln und beim Dereferenzieren automatisch die eigene Adresse dazuzählen.
    Halte ich aber für arg fricklig und funktioniert natürlich nur für eigene Klassen, die auch Gebrauch dieser relativen Zeiger machen.

    Du kannst dir allerdings beim Mappen des shared memory eine Adresse wünschen. Ob du die bekommst, hängt aber vom OS ab und geht sowieso nur, wenn in diesem Bereich noch kein Speicher reserviert ist.
    Falls du es aber schaffst, den Bereich in beiden Prozessen an der gleichen Stelle zu mappen, kannst du nach Herzenslust Objekte im shared memory konstruieren, sofern sie eigene Allokatoren unterstützen (dazu zählen schon einmal Containerklassen aus der Standardlib und viele aus boost).



  • Okay,

    -Also ich habe shared mem: mem.
    -Da ist ein Objekt drinen von der Klasse
    class ...
    {
    ..
    void *ptr;
    ...
    }
    Der erste Prozess P1 setzt ptr auf eine Phsyikalische Speicherstelle A mit
    der Virtuellen Pointernummer 1111.
    Der zweite Prozess P2 möchte die Speicherstelle A nun schreiben. Beim dereferenzieren von 1111 kommt er jedoch nicht unbedint bei A raus (Sehr wahrscheinlich sogar nicht), weil seine Virtuelle Pointernummer 1111 auf einen Pysikalischen Speicher B zeigen kann.

    Man kann das umgehen in dem man differenzen (Abstände) nutzt. ???
    -mem hat in Prozess P1 die Virtuelle Speicheraddresse 2222=mem(P1)
    -mem hat in Prozess P2 die Virtuelle Speicheraddresse 3333=mem(P2)

    P1 setzt ptr auf ptr=1111-2222=A(P1)-mem(P1). (1111=A(P1) aus der Sicht von P1)
    P2 dereferenziert 3333+ptr=mem(P2)+( A(P1)-mem(P1) ) ????

    Und das geht weil:
    A(P1)-mem(P1)=A(P2)-mem(P2)=A(Pn)-mem(Pn)=const ???
    -> Das muss so sein, weil mem(P1) auf den selben Pysikalischen Speicher zeigt wie mem(P2), jedoch wahrscheinlich durch zwei unterschiedelich virtuelle Pointer in beiden Prozessen P1 und P2 dargestellt wird.



  • AlexanderKiebler schrieb:

    Und das geht weil:
    A(P1)-mem(P1)=A(P2)-mem(P2)=A(Pn)-mem(Pn)=const ???
    -> Das muss so sein, weil mem(P1) auf den selben Pysikalischen Speicher zeigt wie mem(P2), jedoch wahrscheinlich durch zwei unterschiedelich virtuelle Pointer in beiden Prozessen P1 und P2 dargestellt wird.

    Ja, genau.



  • THX



  • Hier mal ein Minimalbeispiel, das eine rudimentäre Liste im shared memory erzeugt und im zweiten Prozess wieder ausgibt:

    #include <iostream>
    #include <boost/interprocess/shared_memory_object.hpp>
    #include <boost/interprocess/mapped_region.hpp>
    using namespace std;
    using namespace boost::interprocess;
    
    typedef unsigned char byte;
    
    const int sharedMemorySize=1<<20;
    void* sharedMemory=0;
    
    class MySharedMemAllocator
    {
      public:
        MySharedMemAllocator() : currentOffset(4) {}
        void* allocate(uint size)
        {
          if (currentOffset+size>sharedMemorySize)return 0;
          void* mem=sharedMemory+currentOffset;
          currentOffset+=size;
          return mem;
        }
    
        void deallocate(void*) {}
    
      private:
        uint currentOffset;
    };
    
    MySharedMemAllocator alloc;
    
    void* operator new(size_t s,MySharedMemAllocator& a)
    {
      return a.allocate(s);
    }
    
    void operator delete(void* p,MySharedMemAllocator& a)
    {
      return a.deallocate(p);
    }
    
    void* operator new[](size_t s,MySharedMemAllocator& a)
    {
      return a.allocate(s);
    }
    
    void operator delete[](void* p,MySharedMemAllocator& a)
    {
      return a.deallocate(p);
    }
    
    template <class T> class relative_ptr
    {
      public:
        relative_ptr() : offset(0) {}
        relative_ptr(T* ptr) : offset(ptrdiff(ptr,sharedMemory)) {}
        T& operator*() {return *(T*)(sharedMemory+offset);}
        T* operator->() const {return (T*)(sharedMemory+offset);}
        T* get() const {return (T*)(sharedMemory+offset);}
    
      private:
        ptrdiff_t offset;
        static ptrdiff_t ptrdiff(void* a,void* b)
        {
          return reinterpret_cast<byte*>(a)-reinterpret_cast<byte*>(b);
        }
    };
    
    template <class T> class shm_list
    {
      public:
        shm_list() : head(0,&tail), tail(&head,0) {}
    
        struct shm_node
        {
          shm_node() {}
          shm_node(relative_ptr<shm_node> prev,relative_ptr<shm_node> next,const T& obj=T()) : prev(prev), next(next), data(obj) {}
          relative_ptr<shm_node> prev;
          relative_ptr<shm_node> next;
          T data;
        };
    
        void push_back(const T& obj)
        {
          tail.prev->next=new(alloc)shm_node(tail.prev,&tail,obj);
          tail.prev=tail.prev->next;
        }
    
      //private:
        shm_node head;
        shm_node tail;
    };
    
    const bool processA=true;
    const bool processB=!processA;
    
    int main()
    {
        shared_memory_object shm(open_or_create,"shared_memory",read_write);
        offset_t currentSize;
        if (!shm.get_size(currentSize))return 1;
        if (currentSize<sharedMemorySize)shm.truncate(sharedMemorySize);
        mapped_region region(shm,read_write,0,sizeof(int),0);
    
        sharedMemory=region.get_address();
    
        if (processA)
        {
          relative_ptr<shm_list<int> > list=new(alloc)shm_list<int>;
          for (int i=0;i<100;i++)list->push_back(i*i);
        }
        else
        {
          relative_ptr<shm_list<int> > list(reinterpret_cast<shm_list<int>*>(sharedMemory+4));
          shm_list<int>::shm_node* current=list->head.next.get();
          while (current!=&list->tail)
          {
            cout << current->data << endl;
            current=current->next.get();
          }
        }
    
        if (processB)shared_memory_object::remove("shared_memory");
    }
    

    Man sieht, dass sharedMemory global ist, weswegen ich das auch für fricklig halte.
    Mir ist bisher keine Möglichkeit eingefallen, das ganze vernünftig in einer Klasse zu kapseln, da relative_ptr selbst keinen Zeiger auf irgendwelche Sharedmemory-Wrapperinstanzen speichern darf. Man könnte zwar bei jeder Dereferenzierung einen Lookup bei einem Shared Memory-Verwaltungssingleton o.ä. machen, damit ginge aber die Performanz flöten.



  • Danke nochmal 🙂
    Schaus mir gerade mal durch.



  • Puh ja das ist verzwickt 🙂
    Wenn du ne Lösung hast, sagst du bescheid ??

    Das Problem ist dass
    relative_ptr
    wissen müßte von welchem Prozess es dereferenziert wird, wenn es
    den sharedMemory pointer speicher wollte.
    (Also zu jedem vorhandenen Prozess welcher mit relative_ptr pointer arbeitet
    müßte es einen sharedMemory pointer geben, welchen man zum dereferenzieren bräuchte.)

    Wie du schon sagtest in singletone (wobei schützt das auch vor zwei verschiedenen Prozessen ??? Ich kann doch trotz singletone pro Prozess eine Instanz erstellen) die jedem relative_ptr bekannt wäre nachgucken welche virtuelle speicherzahl auf den
    shared Mem Anfangsbereich zeigt in Abhängigkeit des Prozesses.(Die Flötende Performanz)...
    Oder eben in jedem Prozess was globales hier
    void * sharedMemory

    ev.einfach nan namespace drumrum machen und gut ??

    Noch mutexen ^^

    Gruß


Anmelden zum Antworten