linux下简易插件系统的实现

插件系统所具有的功能

1、 负责插件的加载,检测,初始化。
2、 负责服务的注册。
3、 负责服务的调用。
4、 服务的管理。

运行环境

运行环境:ubuntu20
代码用到的组件:log4cpp

共享库生成

项目组成

cmakelists

# 设置release版本还是debug版本
cmake_minimum_required(VERSION 3.16)
if(${CMAKE_BUILD_TYPE} MATCHES "Release")
    MESSAGE(STATUS "Release版本")
    SET(BuildType "Release")
else()
    SET(BuildType "Debug")
    MESSAGE(STATUS "Debug版本")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -g")
include_directories(
    /usr/local/include/orocos/
    /usr/local/include/
    /usr/include
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
#设置lib库目录
SET(RELEASE_DIR ${PROJECT_SOURCE_DIR}/release)
# debug和release版本目录不一样
#设置生成的so动态库最后输出的路径
SET(LIBRARY_OUTPUT_PATH ${RELEASE_DIR})
ADD_COMPILE_OPTIONS(-fPIC)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
# AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
# 默认生成静态库链接库Dir1
#ADD_LIBRARY (Dir1 ${DIR_LIB_SRCS})
# SHARED生成动态库
# ADD_LIBRARY (Test SHARED  ${DIR_LIB_SRCS})
ADD_LIBRARY (Test3 SHARED  test.h test.cpp plugin.h)
target_link_libraries(Test3 dl liborocos-log4cpp.so pthread)

plugin.conf(用于配置log4cpp)

log4cpp.rootCategory=DEBUG, root
log4cpp.rootCategory.Mgr=, pluginMgr
log4cpp.appender.root=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.root.layout=org.apache.log4cpp.PatternLayout
log4cpp.appender.root.layout.ConversionPattern=[%d{%Y-%m-%d %H:%M:%S.%l} - %p] (%c): %m%n
log4cpp.appender.Mgr=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.Mgr.layout=org.apache.log4cpp.PatternLayout

plugin.h

//plugin.h
#ifndef __PLUGIN_H__
#define __PLUGIN_H__
#include<memory>
#include<string>
#include<iostream>
#include<list>
using namespace std;
class IPlugin
{
public:
  IPlugin() = default;
  virtual ~IPlugin() = default;
  IPlugin(const IPlugin&) = delete;
  IPlugin& operator=(const IPlugin&) = delete;
public:
   //获取支持的插件方法
  virtual const list<string>& getSupportCommandList() const {return supportCommandList_; }

public:
  virtual string getVersionString() const = 0;
  //! \brief 获取业务名称
  //! \return
  virtual string getPluginName() const = 0;
 
  enum pluginState_type
  {
    idle_,
    starting_,
    started_,
    stopping_,
    stopped_,
    excepting_,
  };
  //! 获取插件的运行状态。
  //! \return
  virtual pluginState_type getPluginState() const = 0;
  //! 获取插件的运行状态的解释
  //! \param state
  //! \return
  static string getPluginStateMessage(const pluginState_type& state)
  {
    string msg = "no msg.";
    switch (state)
    {
      case pluginState_type::idle_:
        msg = "plugin is on idle statement.";
        break;
      case pluginState_type::starting_:
        msg = "plugin is on starting statement.";
        break;
      case pluginState_type::started_:
        msg = "plugin is on started statement.";
        break;
      case pluginState_type::stopping_:
        msg = "plugin is on stopping statement.";
        break;
      case pluginState_type::stopped_:
        msg = "plugin is on stopped statement.";
        break;
      case pluginState_type::excepting_:
        msg = "plugin is on excepting statement.";
        break;
    }
    return msg;
  }
  //! \brief 执行业务的主要接口方法
  //! \param msg 由外部程序发送的通讯协议(比如json报文)
  //! \return 错误信息
  virtual void exec(const string& msg) = 0;
  //! \brief 业务停止运行
  //! \return
  virtual void stop() { return ; }
  //! \brief 释放动态库资源
  //! \return 错误信息
  virtual void release() = 0;
protected:
  list<string> supportCommandList_;
};

#endif 

test.h

//test.h
#ifndef __TRIANGLE_H__
#define __TRIANGLE_H__

#include "plugin.h"
#include "log4cpp/Category.hh"
#include "log4cpp/PropertyConfigurator.hh"
#include "log4cpp/NDC.hh"
#include <iostream>

class Test3 : public IPlugin
{ 
public:
  Test3();
  virtual string getVersionString() const;
  //! \brief 获取业务名称
  //! \return
  virtual string getPluginName() const;
public:
  //! 获取插件的运行状态。
  //! \return
  virtual pluginState_type getPluginState() const;
  //! \brief 执行业务的主要接口方法
  //! \param msg 由外部程序发送的通讯协议(比如json报文)
  //! \return 错误信息
  virtual void exec(const string& msg);
  //! \brief 业务停止运行
  //! \return
  virtual void stop();
  //! \brief 释放动态库资源
  //! \return 错误信息
  virtual void release();
protected:
  list<string> supportCommandList_;
  log4cpp::Category *m_log;;
};

#endif /* __TRIANGLE_H__ */

test.cpp

//test.cpp
#include "test.h"
extern "C"
{
    void * getInstance()
    {
        return new Test3;
    }
}
Test3::Test3()
{
  try
  {
    log4cpp::PropertyConfigurator::configure("../plugin.conf");
  }
  catch (log4cpp::ConfigureFailure &f)
  {
      std::cout << "Configure Problem " << f.what() << std::endl;
      exit(1);
  }
  m_log = &log4cpp::Category::getInstance(std::string("Test3"));
}
Test3::pluginState_type Test3::getPluginState() const
{
    return Test3::starting_;
}
string Test3::getVersionString() const
{
    return "3.0";
}
string Test3::getPluginName() const
{
    return "Test3";
}
void Test3::exec(const string& msg)
{
    m_log->debug("execute command %s",msg.c_str());
}
void Test3::stop() 
{ 
    m_log->debug("program stop");
}
void Test3::release()
{
    m_log->debug("release plugram");
}

编译后即可在…/release中生成libTest3.so共享库。

插件系统具体实现

项目整体
cmakelists

cmake_minimum_required(VERSION 3.5)
project(plugin LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -W -Wall -Wextra -g")
include_directories(
    /usr/local/include/orocos/
    /usr/local/include/
    /usr/include
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
add_executable(plugin main.cpp plugin.h pluginMgr.cpp pluginMgr.h)
target_link_libraries(plugin dl liborocos-log4cpp.so pthread)

plugin.config

log4cpp.rootCategory=DEBUG, root
log4cpp.rootCategory.Mgr=, pluginMgr

log4cpp.appender.root=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.root.layout=org.apache.log4cpp.PatternLayout
log4cpp.appender.root.layout.ConversionPattern=[%d{%Y-%m-%d %H:%M:%S.%l} - %p] (%c): %m%n

log4cpp.appender.Mgr=org.apache.log4cpp.ConsoleAppender
log4cpp.appender.Mgr.layout=org.apache.log4cpp.PatternLayout

pluginMgr.h

//pluginMgr.h
#ifndef __PLUGINMGR_H__
#define __PLUGINMGR_H__

#include <unordered_map>
#include<string>
#include <dirent.h>
#include <dlfcn.h>
#include"plugin.h" 
#include "log4cpp/Category.hh"
#include "log4cpp/PropertyConfigurator.hh"
#include "log4cpp/NDC.hh"

#define workPath "../lib/"
#define MIN_FILE_LENGTH   3
#define LOGGING_ERROR     printf
#define LOGGING_WARN      printf
#define LOGGING_DEBUG     printf
#define PROJECT_NAME "PluginTest"
#define PROJECT_VERSION "1.0"

class  PluginManager 
{
private:
  PluginManager();
public:
  static PluginManager& getInstance();
  ~PluginManager();
  static const char* getLibraryName();
  static const char* getLibraryVersion();
public:
  void loadAll();
  void unloadAll();
  bool isloadedFromPath(string soFileName);
  bool loadFromPath(const string& libPath);
  void *resolve(void *handle,string className);
  bool unloadFromPath(const string& name);
  list<string> getNames() const ;
  IPlugin* get(const string& name);
private:
  unordered_map<string, IPlugin*> m_plugins;
  unordered_map<string, pair<string,void *>> m_libs;
  log4cpp::Category *m_log;
};
#endif

pluginMgr.cpp

//pluginMgr.cpp
#include"pluginMgr.h"

typedef void *(*instance)();


PluginManager::PluginManager()
{
  try
  {
    log4cpp::PropertyConfigurator::configure("../plugin.conf");
  }
  catch (log4cpp::ConfigureFailure &f)
  {
      std::cout << "Configure Problem " << f.what() << std::endl;
      exit(1);
  }
  m_log = &log4cpp::Category::getInstance(std::string("pluginMgr"));
}
PluginManager &PluginManager::getInstance()
{
  static PluginManager w;
  return w;
}

PluginManager::~PluginManager()
{
  unloadAll();
}
void PluginManager::loadAll()
{
  struct dirent *entry;
  DIR *dp;
  
  dp = opendir(workPath);
  if (dp == NULL) {
    m_log->error("opendir error,work path : %s\n",workPath);
    return ;
  }
  
  while((entry = readdir(dp)))
  {
    string files = entry->d_name;
    if(files.length() > MIN_FILE_LENGTH && files.substr(files.length()-3,3) == ".so")
    {
      string libPath = workPath+files;
      if(isloadedFromPath(libPath))
      {
        m_log->warn("%s had been loaded before.", libPath.c_str());
        continue;
      }
      loadFromPath(libPath);
    }
  }
  closedir(dp);
}
void PluginManager::unloadAll()
{
  for(auto iter = m_plugins.begin();iter!=m_plugins.end();iter++)
  {
    iter->second->release();
  } 
  for(auto iter = m_libs.begin();iter!=m_libs.end();iter++)
  {
    m_log->debug(iter->first.c_str()); 
    pair<string,void*>plugin = iter->second;
    void *handle = plugin.second;
    dlclose(handle);
  }
  m_plugins.clear();
  m_libs.clear();
  m_log->debug("%s unloaded all",PROJECT_NAME);
}


bool PluginManager::loadFromPath(const string& libPath)
{
  void *handle = dlopen(libPath.c_str(), RTLD_LAZY);
  if(handle)
  {
    instance getPluginInstance = (instance)resolve(handle,"getInstance");
    if(getPluginInstance)
    {
      auto plugin = (IPlugin *)getPluginInstance();
      if(plugin)
      {
        auto pluginName = plugin->getPluginName();
        if (m_plugins.count(pluginName))
        {
          m_log->warn("%s repeated loading.", pluginName.c_str());
          unloadFromPath(libPath);
          return false;
        }
        m_plugins[pluginName] =  plugin;
        m_libs[libPath] = pair<string,void *>(pluginName,handle);
        m_log->debug("load plugin name : %s , version: %s", plugin->getPluginName().c_str(),
                    plugin->getVersionString().c_str());
      }
      else
      {
        m_log->error("%s object create failed.", libPath.c_str());
        unloadFromPath(libPath);
        return false;
      }
    }
    else
    {
      m_log->error("%s cannot find symbol.", libPath.c_str());
      unloadFromPath(libPath);
      return false;
    }
  }
  else
  {
     m_log->error("%s load failed.",libPath.c_str());
    return false;
  }
  return true;
}

void* PluginManager::resolve(void *handle,string className)
{
  return dlsym(handle, className.c_str());
}

bool PluginManager::isloadedFromPath(string soFileName)
{
  auto iter = m_libs.find(soFileName);
  if(iter==m_libs.end()) return false;
  return true;
}

bool PluginManager::unloadFromPath(const string& name)
{

  pair<string,void *> plugin  = m_libs[name];
  void *handle = plugin.second;
  string pluginName = plugin.first;
  IPlugin *temp = m_plugins[pluginName];
  if(!handle || !temp)
  {
    m_log->error("%s : unload errer !,dont have handle.",name.c_str());
    return false;
  }
  temp->release();
  dlclose(handle);
  for(auto iter = m_plugins.begin();iter!=m_plugins.end();iter++)
  {
      if(iter->first == pluginName)
      {
          m_plugins.erase(iter);
      }
  }
  for(auto iter = m_libs.begin();iter!=m_libs.end();iter++)
  {
      if(iter->first == name)
      {
          m_libs.erase(iter);
      }
  }
  m_log->debug("%s  unload successfully . ",name.c_str());
  return true;
}

IPlugin *PluginManager::get(const string &name)
{
  if (m_plugins.find(name)!=m_plugins.end())
  {
    m_log->debug("%s get successfully.", name.c_str());
    return m_plugins[name];
  }
 
  m_log->error("%s is not found.", name.c_str());
  return nullptr;
}
list<string> PluginManager::getNames() const 
{
  list<string> res;
  for(auto iter = m_plugins.begin();iter!=m_plugins.end();iter++)
  {
    res.push_back(iter->first);
  }
  return res;
}
const char *PluginManager::getLibraryName()
{
  return PROJECT_NAME;
}
const char *PluginManager::getLibraryVersion()
{
  return PROJECT_VERSION;
}

plugin.h

//plugin.h
#ifndef __PLUGIN_H__
#define __PLUGIN_H__

#include<memory>
#include<string>
#include<iostream>
#include<list>
using namespace std;


#ifndef SERVICEPROJECT_IPLUGIN_H
#define SERVICEPROJECT_IPLUGIN_H

 
class IPlugin
{
public:
  IPlugin() = default;
  virtual ~IPlugin() = default;
 
  IPlugin(const IPlugin&) = delete;
  IPlugin& operator=(const IPlugin&) = delete;
 
public:
   //获取支持的插件方法
  virtual const list<string>& getSupportCommandList() const {return supportCommandList_; }
 
 
public:
  virtual string getVersionString() const = 0;
  //! \brief 获取业务名称
  //! \return
  virtual string getPluginName() const = 0;
 
  enum pluginState_type
  {
    idle_,
    starting_,
    started_,
    stopping_,
    stopped_,
    excepting_,
  };
  //! 获取插件的运行状态。
  //! \return
  virtual pluginState_type getPluginState() const = 0;
  //! 获取插件的运行状态的解释
  //! \param state
  //! \return
  static string getPluginStateMessage(const pluginState_type& state)
  {
    string msg = "no msg.";
    switch (state)
    {
      case pluginState_type::idle_:
        msg = "plugin is on idle statement.";
        break;
      case pluginState_type::starting_:
        msg = "plugin is on starting statement.";
        break;
      case pluginState_type::started_:
        msg = "plugin is on started statement.";
        break;
      case pluginState_type::stopping_:
        msg = "plugin is on stopping statement.";
        break;
      case pluginState_type::stopped_:
        msg = "plugin is on stopped statement.";
        break;
      case pluginState_type::excepting_:
        msg = "plugin is on excepting statement.";
        break;
    }
 
    return msg;
  }
 
  //! \brief 执行业务的主要接口方法
  //! \param msg 由外部程序发送的通讯协议(比如json报文)
  //! \return 错误信息
  virtual void exec(const string& msg) = 0;
  //! \brief 业务停止运行
  //! \return
  virtual void stop() { return ; }
  //! \brief 释放动态库资源
  //! \return 错误信息
  virtual void release() = 0;
protected:
  list<string> supportCommandList_;
};
#endif  // SERVICEPROJECT_IPLUGIN_H
#endif

main.cpp

//main.cpp
#include <iostream>
#include"plugin.h"
#include"pluginMgr.h"
#include <dirent.h>

using namespace std;

int main()
{
  PluginManager::getInstance().loadAll();

  IPlugin *plugin = PluginManager::getInstance().get("Test1");
  plugin->exec("Test1");
  plugin->stop();

  plugin = PluginManager::getInstance().get("Test2");
  plugin->exec("Test2");
  plugin->stop();

  plugin = PluginManager::getInstance().get("Test3");
  plugin->exec("Test3");
  plugin->stop();
  
  PluginManager::getInstance().unloadAll();

  PluginManager::getInstance().loadFromPath("../lib/libTest1.so");
  plugin = PluginManager::getInstance().get("Test1");
  plugin->exec("Test1");
  PluginManager::getInstance().unloadFromPath("../lib/libTest1.so");
}

程序运行截图
程序运行截图
可见插件系统正常运行。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值