2011-10-11 299 views
4

我有一个有趣的问题,似乎在我的互联网研究中尚未解决。 我试图从dlfcn.h函数中动态加载我的C++项目中的库。问题是,当我尝试在运行时重新加载插件(因为我对它们中的任何一个进行了更改),当调用dlclose()时,主程序崩溃(分段错误(核心转储))。 这是我的例子能重现错误:dlclose在复制动态库时崩溃

main.cpp中:

#include <iostream> 
#include <dlfcn.h> 
#include <time.h> 
#include "IPlugin.h" 

int main() 
{ 
    void * lib_handle; 
    char * error; 

    while(true) 
    { 
     std::cout << "Updating the .so" << std::endl; 

     lib_handle = dlopen("./test1.so", RTLD_LAZY); 
     if (! lib_handle) 
     { 
      std::cerr << dlerror() << std::endl; 
      return 1; 
     } 

     create_t fn_create = (create_t) dlsym(lib_handle, "create"); 

     if ((error = dlerror()) != NULL) 
     { 
      std::cerr << error << std::endl; 
      return 1; 
     } 

     IPlugin * ik = fn_create(); 
     ik->exec(); 

     destroy_t fn_destroy = (destroy_t) dlsym(lib_handle, "destroy"); 

     fn_destroy(ik); 

     std::cout << "Waiting 5 seconds before unloading..." << std::endl; 

     sleep(5); 
     dlclose(lib_handle); 
    } 

    return 0; 
} 

IPlugin.h:

class IPlugin 
{ 
public: 
    IPlugin() { } 
    virtual ~IPlugin() { } 
    virtual void exec() = 0; 
}; 

typedef IPlugin * (* create_t )(); 
typedef void (* destroy_t )(IPlugin *); 

Test1.h:

#include <iostream> 
#include "IPlugin.h" 

class Test1 : public IPlugin 
{ 
public: 
    Test1(); 
    virtual ~Test1(); 

    void exec(); 
}; 

Test1.cpp :

#include "Test1.h" 

Test1::Test1() { } 

Test1::~Test1() { } 

void Test1::exec() 
{ 
    std::cout << "void Test1::exec()" << std::endl; 
} 

extern "C" 
IPlugin * create() 
{ 
    return new Test1(); 
} 

extern "C" 
void destroy(IPlugin * plugin) 
{ 
    if(plugin != NULL) 
    { 
     delete plugin; 
    } 
} 

要编译:

g++ main.cpp -o main -ldl 
g++ -shared -fPIC Test1.cpp -o plugin/test1.so 

当例如我改变在测试1东西:: exec方法(改变串要被打印或评论的直线)时发生该问题并且在主程序睡我复制新的test1.so到主要运行目录(cp)。如果我使用move命令(mv),则不会发生错误。使用cp或mv的区别是什么?有什么办法可以解决这个问题,或者用cp来解决这个问题吗?

我用g ++(GCC)4.5.1 20100924(Red Hat 4.5.1-4)使用Fedora 14。

在此先感谢。

+0

我想这是因为'RTLD_LAZY',试着用'RTLD_NOW'。 – Dani

+0

优秀的问题组合。如果只有所有新成员都擅长编写质量问题! – sehe

+0

我已经用RTLD_NOW和上面提到的两个选项(RTLD_LAZY和RTLD_NOW)对RTLD_GLOBAL和RTLD_LOCAL进行了OR操作,然后对它进行了测试。结果保持不变... – carlosms

回答

5

cpmv之间的区别是有关这个问题如下:

  1. cp打开目标文件和新的内容写入它。因此,用新内容替换旧内容
  2. mv不接触原始文件的内容。相反,它使目录入口指向新文件。

事实证明这很重要。在应用程序正在运行时,操作系统保持对可执行文件和共享对象的打开句柄。当需要查阅这些文件之一时,它将使用相关句柄来访问文件的内容。

  1. 如果你已经使用cp,内容已被损坏,所以任何事情都有可能发生(段错误是一个很可能的结果)。
  2. 如果您已使用mv,则打开的文件句柄仍然指原始文件,即使不再有目录条目,该文件仍然存在于磁盘上。

如果你使用过mv更换共享对象,你应该能够dlclose旧的和dlopen新的。但是,这不是我已经完成或推荐的。

+1

实现这一目标的最佳方法通常是使用'install'命令。 – mloar

-1

试试这个:

extern "C" 
void destroy(IPlugin * plugin) 
{ 
    if(plugin != NULL && dynamic_cast<Test1*>(plugin)) 
    { 
     delete static_cast<Test1*>(plugin); 
    } 
} 
+0

这应该是一个评论,而不是一个答案。 – bmargulies