Linux中的共享库中的单例不能按预期工作

问题描述 投票:2回答:1

我创建了包含单例类的简单共享库,我希望这个类能够相应地运行,即它将成为所有将使用它的应用程序的真正单例。但实际上事实证明它的工作方式不同。使用我的共享库的每个应用程序都会创建自己的单例实例,这根本不符合我的计划。

这是共享库的代码:

singleton.h

#ifndef SINGLETON_H
#define SINGLETON_H

#if defined _WIN32 || defined __CYGWIN__
  #ifdef BUILDING_DLL
    #ifdef __GNUC__
      #define DLL_PUBLIC __attribute__ ((dllexport))
    #else
      #define DLL_PUBLIC __declspec(dllexport) // Note: actually gcc seems to also supports this syntax.
    #endif
  #else
    #ifdef __GNUC__
      #define DLL_PUBLIC __attribute__ ((dllimport))
    #else
      #define DLL_PUBLIC __declspec(dllimport) // Note: actually gcc seems to also supports this syntax.
    #endif
  #endif
  #define DLL_LOCAL
#else
  #if __GNUC__ >= 4
    #define DLL_PUBLIC __attribute__ ((visibility ("default")))
    #define DLL_LOCAL  __attribute__ ((visibility ("hidden")))
  #else
    #define DLL_PUBLIC
    #define DLL_LOCAL
  #endif
#endif

class DLL_PUBLIC Singleton
{
public:   
    static Singleton &instance();
    int test();

private:
    Singleton();
    int m_num;
};

#endif

singleton.cpp

#include "singleton.h"

Singleton &Singleton::instance()
{
    static Singleton singleton;
    return singleton;
}

Singleton::Singleton() :
    m_num(0)
{ }

int Singleton::test()
{
  return ++m_num;
}

编译和链接如下:

g++ -c -pipe -fPIC -o singleton.o singleton.cpp
g++ -rdynamic -export-dynamic -shared -Wl,-soname,libSingletonLib.so.1 -o libSingletonLib.so.1.0.0 singleton.o

小测试实用程序:

main.cpp中

#include <stdio.h>
#include "singleton.h"

int main(int argc, char *argv[])
{    
    int num = Singleton::instance().test();
    printf("num: %d", num);
    getchar();

    return 0;
}

以及编译和链接选项:

g++ -c -pipe -g -std=gnu++11 -Wall -W -fPIC -o main.o main.cpp
g++ -Wl -o ../SingletonTest main.o   -L.. -lSingletonLib 

现在我运行测试应用程序的2个实例,所以我希望它们都使用单例,并且数字会增加。但出乎意料的是输出是:

第一个例子:

./SingletonTest 
num: 1

第二审:

./SingletonTest
num: 1

注意:第一个应用程序仍然在第二个应用程序启动时运行。

据我所知,应用程序的每个实例都创建了单例的不同实例。

如何避免这种情况,以便所有与同一共享库链接的实例将只使用一个单例?

我用:

  • Ubuntu 18.04
  • gcc(Ubuntu 7.3.0-27ubuntu1~18.04)7.3.0

更新:好的,看起来我必须使用共享内存或一些进程间通信。我以前从未使用过这个问题,所以我会重新解释一下这个问题:如何在一个过程中使用一个且只有一个单例?我怎么能把它放在共享内存或其他什么?

c++ linux shared-libraries ipc dllexport
1个回答
0
投票

好吧,经过长时间的搜索,我想我已经找到了解决方案(或者可能是解决方法)。出于某种原因,我完全忽略了共享库共享代码但不共享数据。使用相同共享库的每个应用程序都会创建单例的副本。所以我理解我需要按照评论中的建议使用IPC(感谢@ Gojita和@Mayur)。

那是我来的代码:

#include <new>
#include <errno.h>

...

Singleton &Singleton::instance()
{
    //static Singleton instance;
    //return instance;
    static Singleton* instance = getSharedMemory();
    return *instance;
}

Singleton * Singleton::getSharedMemory()
{
    Singleton * instance = nullptr;
    bool already_exists = false;

    int shm = shm_open("my_memory", O_CREAT | O_RDWR | O_EXCL, 0777);
    if(shm == -1)
    {
        if(errno == EEXIST)
        {
            already_exists = true;
            shm = shm_open("my_memory", O_CREAT | O_RDWR , 0777);
        }
        if(shm == -1)
        {
            perror("shm_open error");
            return nullptr;
        }
    }

    void *addr = mmap(nullptr, sizeof(Singleton) + 1, PROT_WRITE | PROT_READ, MAP_SHARED, shm, 0);
    if(!already_exists)
    {
        if (ftruncate(shm, sizeof(Singleton) + 1) == -1)
        {
            perror("ftruncate error");
            return nullptr;
        }
        instance = new(addr) Singleton;
    }
    else
        instance = reinterpret_cast<Singleton *>(addr);


    return instance;
}

注意:在下面的代码中我创建了共享内存区域(名为“my_memory”)。如果内存已经分配(标志O_EXCL完成工作)我只是使用它,否则我设置它的大小(使用ftruncate)并使用在内存中放置new来创建我的单例。作为奖励 - 您可以退出应用程序,然后再次输入并按原样使用单例 - 该类仍保留在内存中。可以通过调用munmapshm_unlink或重新启动操作系统来释放内存。

© www.soinside.com 2019 - 2024. All rights reserved.