Da ich in meinen dynamisch geladenen Bibliotheken seltsames Verhalten von globalen Variablen beobachtet habe, schrieb ich den folgenden Test.Dynamisch geladene Bibliotheken und gemeinsame globale Symbole
Zunächst brauchen wir eine statisch gelinkte Bibliothek: Der Header test.hpp
#ifndef __BASE_HPP
#define __BASE_HPP
#include <iostream>
class test {
private:
int value;
public:
test(int value) : value(value) {
std::cout << "test::test(int) : value = " << value << std::endl;
}
~test() {
std::cout << "test::~test() : value = " << value << std::endl;
}
int get_value() const { return value; }
void set_value(int new_value) { value = new_value; }
};
extern test global_test;
#endif // __BASE_HPP
und die Quelle test.cpp
#include "base.hpp"
test global_test = test(1);
Dann habe ich eine dynamisch geladene Bibliothek schrieb: library.cpp
#include "base.hpp"
extern "C" {
test* get_global_test() { return &global_test; }
}
und ein Client-Programm laden g dieser Bibliothek: client.cpp
#include <iostream>
#include <dlfcn.h>
#include "base.hpp"
typedef test* get_global_test_t();
int main() {
global_test.set_value(2); // global_test from libbase.a
std::cout << "client: " << global_test.get_value() << std::endl;
void* handle = dlopen("./liblibrary.so", RTLD_LAZY);
if (handle == NULL) {
std::cout << dlerror() << std::endl;
return 1;
}
get_global_test_t* get_global_test = NULL;
void* func = dlsym(handle, "get_global_test");
if (func == NULL) {
std::cout << dlerror() << std::endl;
return 1;
} else get_global_test = reinterpret_cast<get_global_test_t*>(func);
test* t = get_global_test(); // global_test from liblibrary.so
std::cout << "liblibrary.so: " << t->get_value() << std::endl;
std::cout << "client: " << global_test.get_value() << std::endl;
dlclose(handle);
return 0;
}
Nun kompilieren ich die statisch geladenen Bibliothek mit
g++ -Wall -g -c base.cpp
ar rcs libbase.a base.o
das dynamisch geladene Bibliothek
g++ -Wall -g -fPIC -shared library.cpp libbase.a -o liblibrary.so
und der Client
g++ -Wall -g -ldl client.cpp libbase.a -o client
Nun beobachte ich: Der Client und die dynamisch geladene Bibliothek besitzen eine andere Version der Variablen global_test
. Aber in meinem Projekt verwende ich cmake. Das Build-Skript sieht wie folgt aus:
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(globaltest)
ADD_LIBRARY(base STATIC base.cpp)
ADD_LIBRARY(library MODULE library.cpp)
TARGET_LINK_LIBRARIES(library base)
ADD_EXECUTABLE(client client.cpp)
TARGET_LINK_LIBRARIES(client base dl)
die erstellten makefile
s Analyse fand ich, dass Cmake den Client mit
g++ -Wall -g -ldl -rdynamic client.cpp libbase.a -o client
baut Dieser endet in einem etwas anderen, aber fatal Verhalten: Die global_test
des Kunden und die dynamisch geladene Bibliothek sind gleich, werden aber am Ende des Programms zweimal zerstört.
Verwende ich cmake falsch? Ist es möglich, dass der Client und die dynamisch geladene Bibliothek das gleiche global_test
verwenden, aber ohne dieses Doppelzerstörungsproblem?
Meine erste Reaktion wäre, die Notwendigkeit für diese globale Variable in Frage zu stellen. –
Ok, in meinem ursprünglichen Programm ist diese globale Variable eine Konstante, die von einer statisch verbundenen Bibliothek bereitgestellt wird. Aber nichtsdestoweniger wird es zweimal in der cmake-Version – phlipsy
zerstört werden Das gleiche Problem würde für jedes Singleton-Muster gelten, so sehe ich kein Problem mit dem globalen – Elemental