linux插件机制,C语⾔插件机制(上)--linux动态库
abruzzi.iteye/blog/739673
前⾔
插件机制可以使得应⽤程序在发布之后,在不经过重新编译的情况下修改应⽤程序的⾏为,这种形式使得应⽤的框架⽐较⼩巧,也可以给⽤户⼀些⾃由(不是完全的⾃由,有⼀定的限制)。Java中,使⽤*.jar或者其他的脚本引擎都可以完成这样的⼯作,如Eclipse那样。在C语⾔中,当然可以使⽤脚本引擎来实现,⽐如emacs,内置⼀个lisp的引擎,⽤户可以⾃⼰为emacs写脚本,访问emacs环境的⼀些组件,从⽽定制emacs.这⾥要讨论的⽆需使⽤脚本引擎,⽽是⽤C语⾔访问动态链接库来实现。
Linux下的动态库
Linux环境中,与windows下⼀样,函数库有两种⽅式:静态库和动态库,静态库参与连接,由于要将⽬标代码.o与lib中的函数符号合并在⼀起,所以最终⽣成的可执⾏⽂件较⼤。⼀般以.a结尾。如libxxx.a。⽽动态库(共享库)则不参与编译,只是在运⾏时才加载如内存,且仅加载⼀次,因此最终的可执⾏⽂件较⼩。事实上,当⼀个可执⾏⽂件需要运⾏动态库中的函数时,系统会在内存中查,如果已经加载,则直接调⽤,否则才做⼀次加载,动态库的结尾⼀般为.so,如libxxx.so。
Linux为动态库的访问提供了4个API,分别为dlopen , dlerror , dlsym 和 dlclose。这些API的原型定义在⽂件dlfcn.h中,其实现则分别对应有两个库⽂件(静态库 libdl.a 和动态库 libdl.so)。
dlopen加载动态库,并返回句柄
dlerror如果加载,访问符号出错,可以通过此接⼝获得详细的描述
dlsym返回⼀个动态库中的符号,即通过函数名获得此函数的指针
dlclose完成之后,释放dlopen返回的句柄
动态库的使⽤较为简单,⽐如动态库的名称为plugina.so,其中包含这样的函数原型:
C代码
int func(int a, int b);
可以看下⼀个例⼦:
C代码
#include
//句柄
void *flib;
//⼊⼝函数原型
int (*pfunc)(int a, int b);
//错误信息字符串
char *error_message;
int plugin_test(){
int a = 1, b = 4;
int result = 0;
//加载plugina.so,以RTLD_LAZY⽅式
flib = dlopen("/home/juntao/.libs/plugina.so", RTLD_LAZY);
error_message = dlerror();
if(error_message){
return (-1);
}
//到函数名为func的函数,返回其指针
*(void **)(&pfunc) = dlsym(flib, "func"); error_message = dlerror();
if(error_message){
return (-1);
}
//调⽤pfunc指向的指针,及func函数
result = pfunc(a, b);
/
/释放
int code = dlclose(flib);
error_message = dlerror();
if(error_message){
return (-1);
}
return 0;
}
编译运⾏
假设plugina.so的源⽂件为plugina.c,内容为:C代码
//file plugina.c
int func(int a, int b){
int c = 0;
c = 3*a + 4*b + 6;
return c;
}
我们将这个.c⽂件编译为.so,命令如下:
//⽣成plugina.o
$gcc -c -fpic plugina.c
//⽣成plugina.so
$gcc -shared -lc -o plugina.so plugina.o
将动态库访问部分的代码存为plugintest.c,然后使⽤下列命令编译:
C代码
$gcc -o plugintest plugintest.c -ldl
-ldl意思是,使⽤库libdl.so,linux下访问搜索路径内的库⽂件⽆需加lib前缀。
将⽣成的plugina.so放⼊路径/home/juntao/.libs/,然后运⾏plugintest。
$./plugintest
result = 25
c语言listinsert函数好了,这⼀次先介绍⼀些基础知识,相信在此基础上,很多朋友都可以⾃⼰设计出⼀些简单实⽤的⽀持“插件”的应⽤来了,我们下⼀次详细讨论⼀个更实际⼀些的例⼦,⼀个计算器的实现,这个计算器只有简单的框剪,所有的运算都通过插件来实现。⽤户可以通过配置⽂件来定制插件的路径,⼊⼝等信息。
---------------------------------------------------------------
我们在这篇⽂章中实现⼀个计算器,计算器程序calc本⾝不做运算,只是将操作数传递给具体的插件(a
dder, suber, muler, diver)来完成实际运算。⾸先,计算器根据插件配置⽂件l来确定插件的位置,名称,⼊⼝符号的定义,然后依次调⽤各个插件完成计算。
插件列表
⽂中涉及到的插件定义在l中,⽂档结构如下:
Xml代码
每个插件为⼀个plugin标签,plugin标签中包含library, entry两个字标签,分别定义动态库⽂件的路径及名称和插件函数的⼊⼝。为了简便,我们不重复设计list及xml解析,这⾥使⽤libxml2作为xml的分析器,GLIB中的GSList(单链表)来作为插件列表的链表对象。
每⼀个插件在C语⾔中的定义如下,⾮常简单(plugin.h)
C代码
#ifndef _PLUGIN_H_
#define _PLUGIN_H_
typedef struct{
char name[64];
char path[256];
char entry[128];
int version;
}Plugin;
#endif
这⾥为了⾏⽂⽅便,Plugin结构中的字符串为静态尺⼨。
计算器
计算器调⽤parseconf模块中的load_plugins将l中定义的Plugin加载进⼀个GSList,以备后⽤:
C代码
#include "plugin.h"
extern int load_plugins(char *config, GSList **list);
插件中的函数原型应该符合接⼝定义:
C代码
//pointer to function, which return a double, and get 2 double as input double (*pfunc)(double a, double b);
计算器的主要代码如下:
C代码
int calc_test(double a, double b){
GSList *list = NULL, *it = NULL;
Plugin *pl = NULL;
//insert a null node into list at first
list = g_slist_append(list, NULL);
int code = 0;
double result;
//load plugin defined l into list
load_plugins("l", &list);
for(it = list; it != NULL; it = it->next){
pl = (Plugin *)it->data;
if(pl == NULL){
continue;
}else{
//open the library
flib = dlopen(pl->path, RTLD_LAZY);
dlError = dlerror();
if(dlError){
fprintf(stderr, "open %s failed\n", pl->name);
g_slist_free(list);
return -1;
}
//get the entry
*(void **)(&pfunc) = dlsym(flib, pl->entry);
dlError = dlerror();
if(dlError){
fprintf(stderr, "find symbol %s failed\n", pl->entry);
g_slist_free(list);
return -1;
}
//call the function
result = (*pfunc)(a, b);
printf("%s(%f, %f) = %f\n", pl->entry, a, b, result);
//then close it
code = dlclose(flib);
dlError = dlerror();
if(code){
fprintf(stderr, "close lib error\n");
g_slist_free(list);
return -1;
}
}
}
g_slist_free(list);
return 0;
}
⾸先,定义⼀个GSList,然后将其传递给load_plugins,load_plugins解析l,然后填充list返回,calc_test遍历插件列表,并调⽤每⼀个插件定义的entry.
除法器
我们来看⼀个具体的插件:做除法的模块
C代码
#include
double div(double a, double b){
if(b == 0){
fprintf(stderr, "div zero error\n");
return -1;
}else{
return a / b;
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论