c语言插件实现原理,C语言实现插件机制
現(xiàn)在越來越多的軟件項(xiàng)目都提供插件機(jī)制,這樣使得軟件的擴(kuò)展性大大增強(qiáng),那么到底插件機(jī)制的實(shí)現(xiàn)是怎么樣的呢?在這里只談?wù)揅語言的實(shí)現(xiàn),其實(shí)C語言實(shí)現(xiàn)插件的例子也很多,像mjpg-streamer就是將輸入輸出做成插件,dm500機(jī)頂盒的主程序enigma也使用了插件機(jī)制,我就是從enigma當(dāng)中學(xué)習(xí)的。好了,這里給一個(gè)簡單的例子來看看如何實(shí)現(xiàn)。
我的設(shè)想是這樣的:有一個(gè)主程序,有一個(gè)插件(.so文件),主程序里面提供一些最基本的功能模塊,而在插件中使用這些功能模塊實(shí)現(xiàn)某些功能。簡單點(diǎn)就是主程序要調(diào)用.so文件中的函數(shù),而.so要調(diào)用主程序中的某些基礎(chǔ)函數(shù)。
為了既能使用插件a.so, 又能使用插件b.so, 還能使用插件c.so,這里要顯式調(diào)用動(dòng)態(tài)鏈接庫,即通過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)? // 這個(gè)函數(shù)會(huì)被.so文件調(diào)用
{
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"); // 得到入口函數(shù)指針
if (PluginExec) {
PluginExec(0, NULL);? // 調(diào)用入口函數(shù)
}
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++編譯器會(huì)給函數(shù)名加上一些亂七八糟的東西,不信你可以試試,然后用objdump去查查看
{
A a;
a.p("in plugin_exec"); // 這個(gè)在主程序中實(shí)現(xiàn)
plugin_show("in plugin.");? // 這個(gè)也在主程序中實(shí)現(xiàn)
return 0;
}
好了,代碼就這些,很簡單,但能說明問題就行了,再寫個(gè)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做成一個(gè).so文件,后面一行是編繹main.cpp,這里要特別注意參數(shù)-Wl,-E,這個(gè)參數(shù)意思是將-E參數(shù)傳遞給鏈接器ld,最終的目的是將main.cpp中的函數(shù)輸出成全局符號(hào),以方便.so文件調(diào)用,如果沒有此參數(shù),那么編繹也不會(huì)有問題,但在運(yùn)行時(shí)dlopen總是會(huì)失敗,原因是無法解決符號(hào)依賴問題。關(guān)于這個(gè)參數(shù)你可以用objdump對(duì)比一下加與不加的結(jié)果差別。
好了,接下來編譯然后運(yùn)行。
#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.
這個(gè)例子只是簡單的實(shí)現(xiàn)打印,但至少已經(jīng)可以看到主程序和.so文件之間可以調(diào)用了,那我再實(shí)現(xiàn)a.so, b.so當(dāng)然也不成問題了。可能有人會(huì)產(chǎn)生疑問,為什么不隱式調(diào)用呢?原因是:如果隱式調(diào)用就必須在編譯階段確定好.so文件,這樣就談不上可擴(kuò)展插件了,它們之間就存在了編譯上的依賴關(guān)系。而顯式調(diào)用是在運(yùn)行期間確定他們的依賴關(guān)系的。
如果有興趣可以參數(shù)mjpg-streamer去學(xué)習(xí),但是mjpg-streamer中的插件沒有調(diào)用主程序的函數(shù),最好的學(xué)習(xí)例子還是enigma,他里面實(shí)現(xiàn)了大量的插件。以后嵌入式軟件項(xiàng)目的擴(kuò)展性要求會(huì)越來越高,插件擴(kuò)展也大受歡迎,插件擴(kuò)展的機(jī)制很多,需要我們?nèi)ナ占瘜W(xué)習(xí),這里講的是最簡單的一種。我為什么要學(xué)習(xí),原因是我目前參與的一個(gè)項(xiàng)目主程序盡然大到30多MB,編譯鏈接時(shí)間太長,要擴(kuò)展功能就更痛苦了,一次一次的編譯/鏈接,一次一次的等待,真是折磨。
附:
enigma是dm500機(jī)頂盒的主程序,enigma本身通過c++實(shí)現(xiàn)gui, gdi, dvb等一堆基礎(chǔ)庫,并實(shí)現(xiàn)了插件管理器,外圍的功能基本全是c/c++插件實(shí)現(xiàn)。
enigma2是dm800的機(jī)頂盒主程序,enigma2就比enigma更高級(jí)了,他通過c++實(shí)現(xiàn)gui, gdi, dvb等一些基礎(chǔ)庫,其余的界面功能,機(jī)頂盒功能全部是用動(dòng)態(tài)語言python實(shí)現(xiàn),中間使用swig膠合在一起。也是不錯(cuò)的學(xué)習(xí)例子,但項(xiàng)目太大,學(xué)習(xí)不太容易。
總結(jié)
以上是生活随笔為你收集整理的c语言插件实现原理,C语言实现插件机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据库创建
- 下一篇: sevlet 注释initparams_