如何处理使用 dlopen() 打开的已更改库

Nar*_*ase 2 c++ linux dll dlopen

我在程序中使用通过 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);
}
Run Code Online (Sandbox Code Playgroud)

编辑 2:库(UA_String 来自open62541)它只是使用 eclipse 构建并复制到 [...]/plugins 中。执行工作正常,直到我覆盖它

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
}
Run Code Online (Sandbox Code Playgroud)

Bas*_*tch 5

你的问题不清楚。

如果你有一些/tmp/plugin.so并且你这样做了

void* dl = dlopen("/tmp/plugin.so", TRL_NOW);
Run Code Online (Sandbox Code Playgroud)

后来(在同一过程中)一些

rename("/tmp/plugin.so", "/tmp/oldplugin.so")
Run Code Online (Sandbox Code Playgroud)

(甚至unlink("/tmp/plugin.so");......)你应该能够dlclose(dl);

但是,如果您的构建过程正在创建一个新的,例如您有一些make /tmp/plugin.so目标,那么您确实应该做一个

 mv /tmp/plugin.so /tmp/plugin.so~ 
Run Code Online (Sandbox Code Playgroud)

甚至

 rm /tmp/plugin.so
Run Code Online (Sandbox Code Playgroud)

在链接共享库之前,例如之前

gcc -shared -Wall -O /tmp/plugin*.pic.o -o /tmp/plugin.so
Run Code Online (Sandbox Code Playgroud)

换句话说,确保您的构建过程不会覆盖(原始)同一 inode中的字节/tmp/plugin.so

/tmp/plugin.so因此,如果您在构建过程中使用某些命令覆盖旧mv /tmp/newplugin.so /tmp/plugin.so命令,您最好在之前执行 amv /tmp/plugin.so /tmp/plugin.so~或 a 。rm /tmp/plugin.so

请注意,mmap(2) (由dlopen(3)在内部调用)实际上是在打开的 inode 上工作。请参阅path_resolution(7)。因此,您可以取消链接(2)您的共享库,同时仍然保留它dlopen

因此,切勿覆盖现有共享库inode中的字节;尽一切必要确保在插件构建过程中创建一个新的共享库索引节点。

阅读高级 Linux 编程和 Drepper 的《如何编写共享库》

顺便说一句,真正的问题与POSIX 系统上的文件描述符(即打开的索引节点)dlopen的性质无关(多个进程可以读写同一个文件;用户或系统管理员或工具开发人员)应该避免造成严重破坏。)。

还可以使用pmap(1)(如pmap 1234)和/或cat /proc/1234/maps了解pid 1234进程的内存映射(即其虚拟地址空间)。

实际上,安装插件的用户或系统管理员应确保为其创建原始 inode,或者没有进程正在使用该插件(在安装之前)。这是他的责任(并且是整个系统问题)。因此,您确实需要教育您的用户或系统管理员,并记录问题,例如在安装插件时建议使用install(1)和/或锁定实用程序(如包管理器)

附言。之前复制共享对象的私有副本dlopen可能会改善情况,但不能解决问题(如果共享对象源在复制过程中更新怎么办?)。真正的错误是在构建过程中,它覆盖了共享对象,而不是编写和创建原始的新索引节点。

  • @Narase 制作该库的副本。然后“dlopen”该副本。一旦“dlopen”成功,就“rm”它。 (2认同)