Сбой dlclose при копировании динамических библиотек - PullRequest
4 голосов
/ 11 октября 2011

У меня есть интересная проблема, которая, кажется, не решена моими исследованиями в Интернете. Я пытаюсь загружать библиотеки динамически в моем проекте c ++ с функциями из dlfcn.h. Проблема в том, что когда я пытаюсь перезагрузить плагины во время работы (потому что я внес изменения в любой из них), происходит сбой основной программы (ошибка сегментации (сбрасывается ядро)) при вызове 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

Проблема возникает, когда, например, я что-то изменяю в методе Test1 :: exec (изменяя строку, которая должна быть напечатана, или комментируя строку), и пока основная программа спит, я копирую новый test1.so в основной рабочий каталог (cp ). Если я использую команду перемещения (mv), ошибки не возникает. В чем разница между использованием cp или mv? Есть ли способ решить эту проблему или сделать это с помощью cp?

Я использую Fedora 14 с g ++ (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4).

Заранее спасибо.

Ответы [ 2 ]

5 голосов
/ 11 октября 2011

Разница между cp и mv, относящаяся к этому вопросу, следующая:

  1. cp открывает файл назначения и записывает в него новое содержимое.Поэтому заменяет старое содержимое новым содержимым.
  2. mv не касается содержимого исходного файла.Вместо этого он делает каталог точкой входа в новый файл.

Это оказывается важным.Во время работы приложения ОС сохраняет открытые дескрипторы исполняемого файла и общих объектов.Когда ему нужно обратиться к одному из этих файлов, он использует соответствующий дескриптор для доступа к содержимому файла.

  1. Если вы использовали cp, содержимое теперь повреждено, поэтому все можетслучиться (segfault - вполне вероятный результат).
  2. Если вы использовали mv, дескриптор открытого файла по-прежнему ссылается на исходный файл, который продолжает существовать на диске, хотя каталога больше нетвведите для него

Если вы использовали mv для замены общего объекта, вы должны иметь возможность dlclose старого и dlopen нового.Однако это не то, что я сделал или рекомендовал бы.

0 голосов
/ 11 апреля 2013

Попробуйте это:

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