为了让应用程序在发不之后不需要重新编译而修改程序行为,我们引入了插件机制,如Linux下的“*.so”、Windows下的“.dll”以及Java中的“.jar”,这种机制让程序的框架趋于小巧、分工明细,完成详细设计文档以后,应用程序的功能接口已经约定完好,根据需要,可以将重要的功能接口封装成插件形式的函数库,根据插件的不同,安排不同的开发人员维护,这样也可以避免因为一群人共用一套代码文件造成同步问题。
函数库分为动态库跟静态库,由于静态库是在链接阶段被整合到可执行文件中去的,程序在运行过程中不依赖静态库,这样一来,每次更新静态库都必须重新链接生成新的可执行程序,这样显然违背了插件机制的初衷,所以我们应该采用运行时链接的动态库来实现插件机制。
Linux下默认的动态库搜索路径是/lib跟/usr/lib,为了让编译器找到指定函数库,需要将动态库拷贝到/lib或者/usr/lib目录下,那么有没有办法指定动态库的绝对路径或者相对路径?答案是肯定的,Linux为动态库的访问提供了4个API,分别是dlopen、dlerror、dlsym和dlclose,这些函数的原型在系统头文件dlfcn.h中定义,其实现分别对应两个库文件(静态库libdl.a和动态库libdl.so)。
dlopen——加载动态库并返回句柄;
dlerror——如果动态库加载失败,返回错误详细消息的指针,否则返回NULL;
dlsym——传入句柄跟函数名,返回函数指针;
dlclose——释放dlopen返回的句柄。
下面演示一个用插件实现加法功能的C语言实例:
一、源代码
1、plugintest.c
#include <stdio.h> #include <dlfcn.h> int main(){ //句柄 void *flib; //入口函数原型 int (*padd)(int a, int b); //错误信息字符串 char *error_message; int a = 2, b = 7, result = 0; //加载plugin.so,以RTLD_LAZY方式 flib = dlopen("./libplugin.so", RTLD_LAZY); error_message = dlerror(); if(error_message) { printf("%s:%d %s\n", __FILE__, __LINE__, error_message); return -1; } //找到函数名为func的函数,返回其指针 padd = dlsym(flib, "add"); error_message = dlerror(); if(error_message) { printf("%s:%d %s\n", __FILE__, __LINE__, error_message); return -1; } //调用padd指向的指针,即add函数 result = padd(a, b); printf("%d + %d = %d\n", a, b, result); //释放 dlclose(flib); error_message = dlerror(); if(error_message) { printf("%s:%d %s\n", __FILE__, __LINE__, error_message); return -1; } return 0; }
2、plugin.c
int add(int a, int b) { return a+b; }
二、编译
//生成plugin.o gcc -fpic -c plugin.c //生成libplugin.so gcc -shared -lc -o libplugin.so plugin.o //生成可执行文件plugintest gcc plugintest.c -o plugintest -ldl
三、运行结果
./plugintest 2 + 7 = 9
除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。