插件的工作原理(9/11)

很多软件为了扩展方便,具备通用性,普遍都支持插件机制:主程序的裸机功能框架不变,各个具体的功能和业务以动态链接库的形式加载进来。这样做的好处是软件发布以后不用重新编译,可以直接通过插件的形式来更新功能,实现软件升值。

插件的本质其实就是共享动态库,只不过组装的形式比较复杂。主程序框架引用的外部模块符号,运行时以动态链接库的形式加载进来并进行重定位,就可以直接调用了。我们只需要将这些功能模块实现,做成支持动态加载的插件,就可以很方便地扩展程序的功能。Linux 提供了专门的系统调用接口,支持显式加载和引用动态链接库。

  1. 加载动态链接库
void *dlopen(const char *filename, int flag);
void *Handle = dlopen("./libtest.so", RTLD_LAZY);

dlopen() 函数返回的是一个 void* 类型的操作句柄,通过这个句柄就可以操作显式加载到内存中的动态库。函数的第一个参数是要打开的动态链接库,第二个参数是打开的标志位:

  • RTLD_LAZY: 解析动态库遇到未定义符号不退出,仍继续使用。
  • RTLD_NOW: 遇到未定义符号,立即退出。
  • RTLD_GLOBAL: 允许导出符号,在后面其他动态库中可以引用。
  1. 获取动态对象的地址
void *dlsym(void *handle, char *symbol);
void (* funcp)(int, int);
funcp = (void(*)(int, int)) dlsym(Handle, "myfunc");

dlsym() 函数根据动态链接库句柄和要引用的符号,返回符号对应的地址。通过这个指针,我们就可以引用动态库里的这个函数或全局变量了。

  1. 关闭动态链接库
int dlclose(void *Handle);

该函数会将加载到内存的共享库的引用计数减一,当引用计数为 0 时,该动态共享库便会从系统中卸载。

  1. 动态库错误函数
const char *dlerror(void);

当动态链接库操作函数失败时,dlerror 将返回出错信息。若没有出错,则 dlerror 返回 NULL。

// sub.c
int add(int a, int b)
{
    
    
	return a + b;

}

int sub(int a, int b)
{
    
    
	return a - b;
}

// main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

typedef int (*cac_func)(int, int);

int main(void)
{
    
    
	void *handle;
	cac_func fp = NULL;
	handle = dlopen("./libtest.so", RTLD_LAZY);
	if(!handle)
	{
    
    
		fprintf(stderr, "%s\n", dlerror());
		exit(EXIT_FAILURE);
	}
	
	fp = dlsym(handle, "add");
	if(fp)
	{
    
    
		printf("add:%d\n", fp(8, 2));
	}

	fp = dlsym(handle, "sub");
	if(fp)
	{
    
    
		printf("sub:%d\n", fp(8, 2));
	}

	dlclose(handle);
	exit(EXIT_SUCCESS);
}
gcc sub.c -fPIC -shared -o libtest.so
gcc main.c -ldl # 如果你的程序中使用dlopen、dlsym、dlclose、dlerror 显示加载动态库,需要设置链接选项 -ldl

猜你喜欢

转载自blog.csdn.net/weixin_39541632/article/details/132264854