c语言插件实现原理,C语言实现插件机制

现在越来越多的软件项目都提供插件机制,这样使得软件的扩展性大大增强,那么到底插件机制的实现是怎么样的呢?在这里只谈论C语言的实现,其实C语言实现插件的例子也很多,像mjpg-streamer就是将输入输出做成插件,dm500机顶盒的主程序enigma也使用了插件机制,我就是从enigma当中学习的。好了,这里给一个简单的例子来看看如何实现。

我的设想是这样的:有一个主程序,有一个插件(.so文件),主程序里面提供一些最基本的功能模块,而在插件中使用这些功能模块实现某些功能。简单点就是主程序要调用.so文件中的函数,而.so要调用主程序中的某些基础函数。

为了既能使用插件a.so, 又能使用插件b.so, 还能使用插件c.so,这里要显式调用动态链接库,即通过dlopen, dlsym, dlclose来使用.so文件,还是直接来看代码。

#mkdir soplugin

#cd soplugin

#vim main.h

//main.h

#ifndef MAIN_H

#define MAIN_H

class A {

public:

A(){};

~A(){};

void p(const char *s);

};

#endif

#vim main.cpp

#include

#include   // dlopen/dlsym/dlclose头文件

#include "main.h"

void A::p(const char *s)

{

printf("A::p()\n");

}

void plugin_show(const char *s)  // 这个函数会被.so文件调用

{

printf("%s\n", s);

}

int main(int argc, char *argv[])

{

int (*PluginExec)(int argc, char *argv);

void *plugin;

printf("loading...\n");

plugin = dlopen("./tplugin.so", RTLD_GLOBAL|RTLD_NOW); // 显式打开.so文件

if (plugin == NULL) {

printf("ptr: %p\n", plugin);

perror("Can not load tplugin.so");

return -1;

}

PluginExec = (int (*)(int, char*))dlsym(plugin, "plugin_exec"); // 得到入口函数指针

if (PluginExec) {

PluginExec(0, NULL);  // 调用入口函数

}

dlclose(plugin);

return 0;

}

#vim plugin.cpp

#include "main.h"

extern void plugin_show(const char *s);

extern "C"

int plugin_exec(int argc, char *argv[])  // 这里要用extern "C"声明,否则C++编译器会给函数名加上一些乱七八糟的东西,不信你可以试试,然后用objdump去查查看

{

A a;

a.p("in plugin_exec"); // 这个在主程序中实现

plugin_show("in plugin.");  // 这个也在主程序中实现

return 0;

}

好了,代码就这些,很简单,但能说明问题就行了,再写个Makefile。

# vim Makefile

all:

g++ -shared -fPIC -DPIC -c plugin.cpp -I.

ld -shared -o tplugin.so plugin.o

g++ -Wl,-E -o mm main.cpp -ldl

前面两行是将plugin.cpp做成一个.so文件,后面一行是编绎main.cpp,这里要特别注意参数-Wl,-E,这个参数意思是将-E参数传递给链接器ld,最终的目的是将main.cpp中的函数输出成全局符号,以方便.so文件调用,如果没有此参数,那么编绎也不会有问题,但在运行时dlopen总是会失败,原因是无法解决符号依赖问题。关于这个参数你可以用objdump对比一下加与不加的结果差别。

好了,接下来编译然后运行。

#make

g++ -shared -fPIC -DPIC -c plugin.cpp -I.

ld -shared -o tplugin.so plugin.o

g++ -Wl,-E -o mm main.cpp -ldl

#./mm

loading...

A::p()

in plugin.

这个例子只是简单的实现打印,但至少已经可以看到主程序和.so文件之间可以调用了,那我再实现a.so, b.so当然也不成问题了。可能有人会产生疑问,为什么不隐式调用呢?原因是:如果隐式调用就必须在编译阶段确定好.so文件,这样就谈不上可扩展插件了,它们之间就存在了编译上的依赖关系。而显式调用是在运行期间确定他们的依赖关系的。

如果有兴趣可以参数mjpg-streamer去学习,但是mjpg-streamer中的插件没有调用主程序的函数,最好的学习例子还是enigma,他里面实现了大量的插件。以后嵌入式软件项目的扩展性要求会越来越高,插件扩展也大受欢迎,插件扩展的机制很多,需要我们去收集学习,这里讲的是最简单的一种。我为什么要学习,原因是我目前参与的一个项目主程序尽然大到30多MB,编译链接时间太长,要扩展功能就更痛苦了,一次一次的编译/链接,一次一次的等待,真是折磨。

附:

enigma是dm500机顶盒的主程序,enigma本身通过c++实现gui, gdi, dvb等一堆基础库,并实现了插件管理器,外围的功能基本全是c/c++插件实现。

enigma2是dm800的机顶盒主程序,enigma2就比enigma更高级了,他通过c++实现gui, gdi, dvb等一些基础库,其余的界面功能,机顶盒功能全部是用动态语言python实现,中间使用swig胶合在一起。也是不错的学习例子,但项目太大,学习不太容易。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值