我在我的程序中使用共享对象,通过dlopen()加载。 当我用mv debug/newLibrary.so plugin/usedLibrary.so
覆盖库时,一旦它试图与加载的库进行交互,我的程序就会崩溃。 我什至不能使用dlclose(),这让我一个SIGSEV。
处理这种情况的最好方法是什么?
操作系统是Linux
编辑:实际的代码
void DynamicallyLoadedLibrary::loadLibrary() { // ModificationTime updaten lastModificationTime = modificationTime(); // Library laden libraryHandle = dlopen(path.c_str(), RTLD_NOW); if (!libraryHandle) { // Library gefunden? throw DynamicLibraryException("Dynamic Library not found: " + path + "\n" + dlerror()); } // Funktion laden externalFunction = (dll_function) dlsym(libraryHandle, "run"); char *error; if ((error = dlerror()) != NULL) { // Funktion gefunden? throw DynamicLibraryException("Dynamic Library not found: run()\n" + string(error)); } } void DynamicallyLoadedLibrary::close() { if (libraryHandle != nullptr) { cout << "DLL/close(): " << dlclose(libraryHandle) << endl; // DEBUG libraryHandle = nullptr; externalFunction = nullptr; } } void DynamicallyLoadedLibrary::operator()(vector<shared_ptr<ServerData>> &data) { // Wenn Datei sich geaendert hat, neu laden if (fileChanged()) { close(); loadLibrary(); } externalFunction(data); }
编辑2:图书馆(UA_String是从open62541
)它简单地与eclipse构build和复制在/插件。 执行正常工作,直到我覆盖它
extern "C" void run(vector<shared_ptr<ServerData>> &data) { cout << "++++ OPC_WORKING_PACKAGE EXTERN ++++" << endl; // XXX for (unsigned int i = 0; i < data.size(); i++){ UA_String *uaString = (UA_String*) data[i]->dataReference(); cout << string((char*) uaString->data, uaString->length) << endl; } cout << "---- OPC_WORKING_PACKAGE EXTERN ----" << endl; // XXX }
你的问题不清楚。
如果你有一些/tmp/plugin.so
,你可以
void* dl = dlopen("/tmp/plugin.so", TRL_NOW);
和后来(在同一个过程中)一些
rename("/tmp/plugin.so", "/tmp/oldplugin.so")
(甚至unlink("/tmp/plugin.so");
…)你应该能够dlclose(dl);
但是,如果您的构建过程正在创建一个新的过程,例如您有一些make /tmp/plugin.so
目标,那么您应该做一个
mv /tmp/plugin.so /tmp/plugin.so~
甚至
rm /tmp/plugin.so
在链接共享库之前,例如之前
gcc -shared -Wall -O /tmp/plugin*.pic.o -o /tmp/plugin.so
换句话说,请确保您的构建过程不会覆盖原始/tmp/plugin.so
相同 inode的字节
所以如果你在构建过程中使用mv /tmp/newplugin.so /tmp/plugin.so
命令覆盖旧的/tmp/plugin.so
,最好使用mv /tmp/plugin.so /tmp/plugin.so~
或rm /tmp/plugin.so
就在之前。
请注意, mmap(2) (由dlopen(3)内部调用)实际上是在打开的inode上工作。 请参阅path_resolution(7) 。 所以你可以解除链接(2)你的共享库,同时仍然使用它。
因此,不要在现有的共享库inode上覆盖字节。 做任何必要的事情,确保在你的插件构建过程中创建一个新的共享库inode 。
阅读高级Linux编程和Drepper的如何编写共享库
顺便说一下,真正的问题与dlopen
无关,而是与POSIX系统上的文件描述符 (即已打开的inode)的性质有关(在这些系统上有几个进程可以读写同一文件;用户或系统管理员或工具开发人员)应该避免破坏破坏)。
也可以使用pmap(1) (如pmap 1234
)和/或cat /proc/1234/maps
来了解pid 1234 进程 (即其虚拟地址空间 )的内存映射。
实际上,安装插件的用户或系统管理员应确保为其创建了原始inode,或者没有进程正在使用该插件(在安装之前)。 这是他的责任(并且是整个系统问题)。 所以你真的需要教育你的用户或者系统管理员,并且记录下这个问题,例如,在安装插件的时候,建议使用install(1)和/或像包管理器这样的锁定工具。
PS。 在dlopen
之前复制私有副本中的共享对象可能会改善这种情况,但并不能解决问题(如果共享对象源在复制过程中得到更新?)。 真正的bug是在构建过程中覆盖共享对象,而不是写入和创建原始的新inode。