Mehrere Instanzen von Singleton über gemeinsam genutzte Bibliotheken unter Linux
Meine Frage, wie der Titel erwähnt, ist offensichtlich, und ich beschreibe das Szenario, in details.
Es gibt eine Klasse namens singleton implementierte singleton-pattern wie folgt in der Datei singleton.h:
/*
* singleton.h
*
* Created on: 2011-12-24
* Author: bourneli
*/
#ifndef SINGLETON_H_
#define SINGLETON_H_
class singleton
{
private:
singleton() {num = -1;}
static singleton* pInstance;
public:
static singleton& instance()
{
if (NULL == pInstance)
{
pInstance = new singleton();
}
return *pInstance;
}
public:
int num;
};
singleton* singleton::pInstance = NULL;
#endif /* SINGLETON_H_ */
dann, es gibt ein plugin namens hello.cpp wie folgt:
#include <iostream>
#include "singleton.h"
extern "C" void hello() {
std::cout << "singleton.num in hello.so : " << singleton::instance().num << std::endl;
++singleton::instance().num;
std::cout << "singleton.num in hello.so after ++ : " << singleton::instance().num << std::endl;
}
können Sie sehen, dass der plugin-Aufruf des singleton, und ändern Sie das Attribut num in singleton.
letzten, es gibt eine main-Funktion verwenden Sie das singleton-und das plugin wie folgt:
#include <iostream>
#include <dlfcn.h>
#include "singleton.h"
int main() {
using std::cout;
using std::cerr;
using std::endl;
singleton::instance().num = 100; //call singleton
cout << "singleton.num in main : " << singleton::instance().num << endl;//call singleton
//open the library
void* handle = dlopen("./hello.so", RTLD_LAZY);
if (!handle) {
cerr << "Cannot open library: " << dlerror() << '\n';
return 1;
}
//load the symbol
typedef void (*hello_t)();
//reset errors
dlerror();
hello_t hello = (hello_t) dlsym(handle, "hello");
const char *dlsym_error = dlerror();
if (dlsym_error) {
cerr << "Cannot load symbol 'hello': " << dlerror() << '\n';
dlclose(handle);
return 1;
}
hello(); //call plugin function hello
cout << "singleton.num in main : " << singleton::instance().num << endl;//call singleton
dlclose(handle);
}
und das makefile ist folgende:
example1: main.cpp hello.so
$(CXX) $(CXXFLAGS) -o example1 main.cpp -ldl
hello.so: hello.cpp
$(CXX) $(CXXFLAGS) -shared -o hello.so hello.cpp
clean:
rm -f example1 hello.so
.PHONY: clean
also, was ist die Ausgabe?
Ich dachte, es ist folgende:
singleton.num in main : 100
singleton.num in hello.so : 100
singleton.num in hello.so after ++ : 101
singleton.num in main : 101
jedoch die tatsächliche Ausgabe ist folgende:
singleton.num in main : 100
singleton.num in hello.so : -1
singleton.num in hello.so after ++ : 0
singleton.num in main : 100
Beweist es, dass es zwei Instanzen der singleton-Klasse.
Warum?
InformationsquelleAutor der Frage bourneli | 2011-12-24
Du musst angemeldet sein, um einen Kommentar abzugeben.
Zunächst sollten Sie verwenden in der Regel
-fPIC
flag beim erstellen von shared libraries.Es nicht mit "works" auf 32-bit-Linux, würde aber nicht auf 64-bit-Version mit einer Fehlermeldung ähnlich der folgenden angezeigt:
Zweiten, wird Ihr Programm so funktioniert, wie Sie erwarten, nachdem Sie hinzufügen
-rdynamic
auf die link-Zeile für das Hauptprogramm:Um zu verstehen, warum
-rdynamic
erforderlich ist, müssen Sie wissen, über die Art und Weise dynamische linker löst Symbole, und über die dynamische symbol Tabelle.Zunächst ein Blick auf das dynamic-symbol-Tabelle für
hello.so
:Dies sagt uns, dass es zwei schwache Funktionsdefinitionen, und eine Globale variable
singleton::pInstance
sichtbar für das dynamische linker.Schauen wir uns nun die statischen und dynamischen symbol-Tabelle für die original
example1
(verbunden, ohne-rdynamic
):Richtig: obwohl die
singleton::pInstance
ist in der ausführbaren Datei wie eine Globale variable, das symbol ist nicht in der dynamische symbol-Tabelle, und daher "unsichtbar" der dynamische linker.Weil der dynamische linker "nicht weiß", dass
example1
enthält bereits eine definition vonsingleton::pInstance
es nicht binden, dass die variable innerhalbhello.so
um die bestehende definition (die ist, was Sie wirklich wollen).Wenn wir
-rdynamic
auf die link-Zeile an:Nun die definition von
singleton::pInstance
innerhalb der main-executable ist sichtbar auf den dynamischen linker, und so wird es "Wiederverwendung", die definition beim ladenhello.so
:InformationsquelleAutor der Antwort Employed Russian
Müssen Sie vorsichtig sein bei der Verwendung, die zur Laufzeit geladen shared libraries. Eine solche Konstruktion ist streng genommen nicht Teil der C++ - standard, und Sie müssen sorgfältig prüfen, was die Semantik eines solchen Verfahrens werden wird.
Zuerst, was passiert ist, dass die gemeinsam genutzte Bibliothek sieht Ihre eigene, separate Globale variable
singleton::pInstance
. Warum ist das so? Eine Bibliothek, die zur Laufzeit geladen wird, ist im wesentlichen ein separates, eigenständiges Programm, das nur geschieht, um nicht einen Einstiegspunkt. Aber alles andere ist wirklich wie ein eigenständiges Programm, und der dynamische Lader zu behandeln wie z.B. initialisieren der globalen Variablen, etc.Den dynamischen loader ist eine Laufzeit-Anlage, die hat nichts zu tun mit der statischen loader. Die statische loader ist Teil der C++ - standard-Implementierung und behebt alle von den wichtigsten Programm-Symbole vor das Hauptprogramm beginnt. Der dynamic loader, auf der anderen Seite, läuft nur nach das Hauptfenster des Programms hat bereits begonnen. Insbesondere alle Symbole des Hauptprogramms bereits gelöst zu sein! Es ist einfach keine Weg, um automatisch ersetzen Sie Symbole aus dem Hauptprogramm dynamisch. Native Programme sind nicht "geschafft" in einer Weise, die ermöglicht eine systematische verbinden. (Vielleicht etwas gehackt werden können, aber nicht in eine systematische, portable Weg.)
So, die eigentliche Frage ist, wie lösen das design-problem, das Sie versuchen. Die Lösung ist hier zu passieren, verarbeitet, um alle globalen Variablen zu der plugin-Funktionen. Machen Sie Ihre Haupt-Programm definieren, das original (und nur die) Kopie der globalen variable, und initialisieren Sie Ihre Bibliothek mit einem Zeiger auf diesen.
Zum Beispiel Ihre gemeinsam genutzte Bibliothek könnte so Aussehen. Fügen Sie zuerst einen pointer-to-pointer auf die singleton-Klasse:
Nun verwenden
*ppInstance
stattpInstance
überall.Im plugin konfigurieren Sie die singleton, um den Zeiger aus dem Hauptprogramm:
Und die main-Funktion, rufen Sie die plugin-Initialisierung:
Nun das plugin teilt den gleichen pointer auf die singleton-Instanz als der rest des Programms.
InformationsquelleAutor der Antwort Kerrek SB
Ich denke die einfache Antwort ist hier:
http://www.yolinux.com/TUTORIALS/LibraryArchives-StaticAndDynamic.html
Wenn Sie eine statische variable, ist es im Objekt gespeichert (.o,.ein und/oder .so)
Wenn das Letzte Objekt ausgeführt werden, enthält zwei Versionen des Objekts, das Verhalten ist unerwartet, wie, zum Beispiel, ruft den Destruktor der Singleton-Objekt.
Mit dem richtigen design, wie die Deklaration der statischen member in der main-Datei und mit-rdynamic/fpic und mit dem "" compiler-Direktiven wird der trick für Sie.
Beispiel makefile-Anweisung:
Hoffe, das funktioniert!
InformationsquelleAutor der Antwort Paulo Lellis
Danke an Euch alle für Eure Antworten!
Als follow-up für Linux haben, können Sie auch verwenden
RTLD_GLOBAL
mitdlopen(...)
proman dlopen
(und die Beispiele, die es hat). Ich habe eine Variante des OP ' s Beispiel in diesem Verzeichnis: github BaumBeispiel-Ausgabe:
output.txt
Quick and dirty:
main
, halten Sie die freigegebenen Objekte um. (z.B., wenn Sie*.so
Objekte importieren in Python)NOLOAD
+GLOBAL
wieder zu öffnen.Code:
Modi:
InformationsquelleAutor der Antwort Eric Cousineau