复制动态库时 dlclose 崩溃
我有一个有趣的问题,我在互联网上的研究似乎没有解决这个问题。 我正在尝试使用 dlfcn.h 中的函数在我的 C++ 项目中动态加载库。问题是,当我尝试在运行时重新加载插件(因为我对其中任何一个进行了更改)时,调用 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;
}
}
To compile:
g++ main.cpp -o main -ldl
g++ -shared -fPIC Test1.cpp -o plugin/test1.so
例如,当我更改 Test1::exec 方法上的某些内容(将字符串更改为打印或注释该行),当主程序休眠时,我将新的 test1.so 复制到主运行目录(cp)。如果我使用移动命令(mv),则不会发生错误。使用 cp 或 mv 有什么区别?有什么办法可以解决这个问题或者使用 cp 来做到这一点吗?
我使用 Fedora 14 和 g++ (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4)。
提前致谢。
I have an interesting problem that seems to be unresolved by my research on the internet.
I'm trying to load libraries dynamically in my c++ project with the functions from dlfcn.h. The problem is that when I try to reload the plugins at running time (because I made a change on any of them), the main program crashes (Segmentation fault (core dumped)) when dlclose() is called.
Here is my example that reproduces the error:
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;
}
}
To compile:
g++ main.cpp -o main -ldl
g++ -shared -fPIC Test1.cpp -o plugin/test1.so
The problem occurs when for example I change something on the Test1::exec method (changing the string to be printed or commenting the line) and while the main program sleeps I copy the new test1.so to main running directory (cp). If I use the move command (mv), no error occurs. What makes the difference between using cp or mv? Is there any way to solve this problem or to do that using cp?
I'm using Fedora 14 with g++ (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4).
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
与此问题相关的
cp
和mv
之间的区别如下:cp
打开目标文件并将新内容写入其中。因此,它用新内容替换旧内容。mv
不会触及原始文件的内容。相反,它使目录条目指向新文件。事实证明这很重要。当应用程序运行时,操作系统保持可执行文件和共享对象的打开句柄。当它需要查阅这些文件之一时,它使用相关句柄来访问文件的内容。
cp
,那么内容现在已被损坏,因此任何事情都可能发生(很可能会出现段错误)。mv
,打开的文件句柄仍然引用原始文件,即使不再有其目录条目,该文件仍继续存在于磁盘上。如果您使用
mv
替换共享对象,则应该能够dlclose
旧的共享对象并dlopen
新的共享对象。然而,这不是我已经做过或推荐的事情。The difference between
cp
andmv
that is pertinent to this question is as follows:cp
opens the destination file and writes the new contents into it. It therefore replaces the old contents with the new contents.mv
doesn't touch the contents of the original file. Instead, it makes the directory entry point to the new file.This turns out to be important. While the application is running, the OS keeps open handles to the executable and the shared objects. When it needs to consult one of the these files, it uses the relevant handle to access the file's contents.
cp
, the contents has now been corrupted, so anything can happen (a segfault is a pretty likely outcome).mv
, the open file handle still refers to the original file, which continues to exist on disk even though there's no longer a directory entry for it.If you've used
mv
to replace the shared object, you should be able todlclose
the old one anddlopen
the new one. However, this is not something that I've done or would recommend.试试这个:
Try this: