复制动态库时 dlclose 崩溃

发布于 2024-12-09 04:46:38 字数 2355 浏览 0 评论 0原文

我有一个有趣的问题,我在互联网上的研究似乎没有解决这个问题。 我正在尝试使用 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

不顾 2024-12-16 04:46:38

与此问题相关的 cpmv 之间的区别如下:

  1. cp 打开目标文件并将新内容写入其中。因此,它用新内容替换旧内容
  2. mv 不会触及原始文件的内容。相反,它使目录条目指向新文件。

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

  1. 如果您使用了cp,那么内容现在已被损坏,因此任何事情都可能发生(很可能会出现段错误)。
  2. 如果您使用了mv,打开的文件句柄仍然引用原始文件,即使不再有其目录条目,该文件仍继续存在于磁盘上。

如果您使用 mv 替换共享对象,则应该能够 dlclose 旧的共享对象并 dlopen 新的共享对象。然而,这不是我已经做过或推荐的事情。

The difference between cp and mv that is pertinent to this question is as follows:

  1. cp opens the destination file and writes the new contents into it. It therefore replaces the old contents with the new contents.
  2. 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.

  1. If you've used cp, the contents has now been corrupted, so anything can happen (a segfault is a pretty likely outcome).
  2. If you've used 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 to dlclose the old one and dlopen the new one. However, this is not something that I've done or would recommend.

时光匆匆的小流年 2024-12-16 04:46:38

试试这个:

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

Try this:

extern "C"
void destroy( IPlugin * plugin )
{
    if( plugin != NULL && dynamic_cast<Test1*>(plugin))
    {
        delete static_cast<Test1*>(plugin);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文