使用到内核里的双向链表技术。
libplugin.c
#include "libplugin.h"
#include
#include
#include
#include
struct plugin_manager *plugin_manager_create() // 创建插件管理链表
{
struct plugin_manager *pm = CALLOC(1, struct plugin_manager); //calloc在动态分配完内存后,自动初始化该内存空间为零
if (!pm) {
printf("malloc failed!\n");
return NULL;
}
INIT_LIST_HEAD(&pm->plugins);//初始化双向链表
return pm;
}
void plugin_manager_destroy(struct plugin_manager *pm)
{
if (!pm) {
return;
}
}
static void *plugin_get_func(struct plugin *p, const char *name)
{
if (!p || !name) {
return NULL;
}
// 函数 dlsym()获取 dlopen()返回的动态加载共享对象的“句柄”以及以空字符结尾的符号名称,并返回将该符号加载到内存中的地址。
// dlsym函数的返回值也就是 获取符号名为name 加载到内存中的地址。 而 struct plugin的首地址是名为name的地址,故可将dlsym的返回地址强制转换为 struct plugin
struct plugin *q = dlsym(p->handle, name);
if (!q) {
printf("dlsym failed:%s\n", dlerror());
return NULL;
}
return q;
}
struct plugin *plugin_lookup(struct plugin_manager *pm, const char *name)
{
if (!pm || !name)
return NULL;
struct list_head* head = NULL;
struct plugin* p = NULL;
list_for_each(head, &pm->plugins) { //它实际上是一个 for 循环,利用传入的 head 作为循环变量,从表头 pm->plugins 开始,逐项向后(next方向)移动 pos,直至又回 pm->plugins.
p = list_entry(head, struct plugin, entry); //获取 head 链表所在结构体的首地址,也就是 struct plugin。
if (0 == strcmp(p->name, name)) { //查找的名字与已存在的名字是否一样
return plugin_get_func(p, name); // 获取 .so 加载到内存中之后 struct plugin所定义变量的首地址。
}
}
return NULL;
}
struct plugin *plugin_load(struct plugin_manager *pm, const char *path, const char *name)
{
struct plugin *sym = NULL;
struct plugin *p = NULL;
void *handle = dlopen(path, RTLD_LAZY); // 打开 .so 文件
if (!handle) {
printf("dlopen failed: %s\n", dlerror());
goto failed;
}
p = plugin_lookup(pm, name);// 目的是判断某个插件是否加载到内存中
if (p) {
printf("plugin %s has already loaded!\n", name);
return p;
}
sym = dlsym(handle, name);// dlsym函数的返回值也就是 获取符号名为name 加载到内存中的地址。 而 struct plugin的首地址是名为name的地址,故可将dlsym的返回地址强制转换为 struct plugin
if (!sym) {
printf("incompatible plugin, dlsym failed: %s\n", dlerror());
goto failed;
}
p = CALLOC(1, struct plugin);
if (!p) {
goto failed;
}
p->handle = handle;
p->name = strdup(name);
p->path = strdup(path);
list_add(&p->entry, &pm->plugins);//添加到链表里,方便查询
return p;
failed:
if (handle) dlclose(handle);
if (p) {
free(p->name);
free(p->path);
free(p);
}
return NULL;
}
void plugin_unload(struct plugin_manager *pm, const char *name) //释放已加载的插件
{
struct plugin *p, *tmp;
list_for_each_entry_safe(p, tmp, &pm->plugins, entry) {
dlclose(p->handle);
list_del(&p->entry); // 链表删除
free(p->name);
free(p->path);
free(p);
}
}
struct plugin *plugin_reload(struct plugin_manager *pm, const char *path, const char *name)
{
plugin_unload(pm, name);
return plugin_load(pm, path, name);
}
libplugin.h
#ifndef LIBPLUGIN_H
#define LIBPLUGIN_H
#include
#include
#ifdef __cplusplus
extern "C" {
#endif
struct version {
int major;
int minor;
int patch;
};
struct plugin {
char *name;
char *path;
struct version version;
void *(*open)(void *arg);
void (*close)(void *arg);
void *(*call)(void *arg0, ...);
void *handle;
struct list_head entry;
};
struct plugin_manager {
struct list_head plugins;
};
struct plugin_manager *plugin_manager_create();
void plugin_manager_destroy(struct plugin_manager *);
struct plugin *plugin_lookup(struct plugin_manager *pm, const char *name);
struct plugin *plugin_load(struct plugin_manager *pm, const char *path, const char *name);
void plugin_unload(struct plugin_manager *pm, const char *name);
struct plugin *plugin_reload(struct plugin_manager *pm, const char *path, const char *name);
/*
* using HOOK_CALL(func, args...), prev/post functions can be hook into func
*/
#define HOOK_CALL(fn, ...) \
({ \
fn##_prev(__VA_ARGS__); \
__typeof__(fn) *sym = dlsym(RTLD_NEXT, #fn); \
if (!sym) {return NULL;} \
sym(__VA_ARGS__); \
fn##_post(__VA_ARGS__); \
})
/*
* using CALL(fn, args...), you need override api
*/
#define CALL(fn, ...) \
({__typeof__(fn) *sym = (__typeof__(fn) *) \
dlsym(RTLD_NEXT, #fn); \
sym(__VA_ARGS__);})
#ifdef __cplusplus
}
#endif
#endif
plugin_ipc.c
#include "../libplugin.h"
static void *ipc_open(void *arg)
{
printf("ipc_open\n");
return NULL;
}
struct plugin plugin_ipc = {
.name = "plugin_ipc",
.version = {1, 0, 0},
.open = ipc_open,
};
test_libplugin.c
#include "libplugin.h"
#include
#include
struct plugin_info {
char *path;
char *name;
};
struct plugin_info p_info[] = {
{"./modules/plugin_log.so", "plugin_log"},
{"./modules/plugin_ipc.so", "plugin_ipc"},
{"./modules/plugin_skt.so", "plugin_skt"},
};
#define SIZEOF(array) (sizeof(array)/sizeof(array[0]))
static struct plugin_manager *pm = NULL;
void init()
{
int i = 0;
struct plugin *p = NULL;
pm = plugin_manager_create(); // 创建插件管理链表
if (!pm) {
printf("plugin_manager_create failed!\n");
return;
}
for (i = 0; i < SIZEOF(p_info); i++) {
p = plugin_load(pm, p_info[i].path, p_info[i].name); //加载动态库。 初始化,并注册到链表里
if (!p) {
printf("plugin_load failed!\n");
return;
}
}
}
void deinit()
{
int i = 0;
for (i = 0; i < SIZEOF(p_info); i++) {
plugin_unload(pm, p_info[i].name); 释放已加载的插件
}
plugin_manager_destroy(pm);
}
void foo()
{
char *name = "plugin_ipc";
struct plugin *p = plugin_lookup(pm, name); // 根据名字,找到struct plugin的首地址
if (!p) {
printf("plugin_lookup %s failed!\n", name);
return;
}
p->open(NULL); // 调用 plugin_ipc 的 open函数
printf("name=%s, version=%d,%d,%d\n", p->name, p->version.major, p->version.minor, p->version.patch);
}
int main(int argc, char **argv)
{
init();
foo();
deinit();
return 0;
}