前言
skynet_module在skynet中非常重要,skynet_context必须有一个module实例。
sknyet_module的作用主要是加载保存动态库,绑定动态库API接口,module代理调用动态库的api接口。
作用:避免的动态库重复加载,接口调用简单化。
skynet_module.h
module结构体非常的简单,就是记录这个库文件名字,句柄,和基本的api接口函数。
struct skynet_context;
typedef void * (*skynet_dl_create)(void);
typedef int (*skynet_dl_init)(void * inst, struct skynet_context *, const char * parm);
typedef void (*skynet_dl_release)(void * inst);
typedef void (*skynet_dl_signal)(void * inst, int signal);
struct skynet_module {
const char * name; //库名称
void * module; //动态库句柄
skynet_dl_create create; //指向库api函数的指针
skynet_dl_init init;
skynet_dl_release release;
skynet_dl_signal signal;
};
void skynet_module_insert(struct skynet_module *mod);
struct skynet_module * skynet_module_query(const char * name);
void * skynet_module_instance_create(struct skynet_module *);
int skynet_module_instance_init(struct skynet_module *, void * inst, struct skynet_context *ctx, const char * parm);
void skynet_module_instance_release(struct skynet_module *, void *inst);
void skynet_module_instance_signal(struct skynet_module *, void *inst, int signal);
void skynet_module_init(const char *path);
skynet_module.c
modules 用来缓存已经加载的动态库缓存到m数组里,以便再次使用时不需要重新加载。
#define MAX_MODULE_TYPE 32
struct modules {
int count;
struct spinlock lock;
const char * path; //动态库查找路径
struct skynet_module m[MAX_MODULE_TYPE];
};
核心函数skynet_moudle_query
cpath = root.."cservice/?.so"
该函数用来查询指定名称的skynet_module,也就是c代码编写的服务,如果还没有加载会从cpath路径里面找。如果找到,会绑定api函数。
mod->create = get_api(mod, "_create");
mod->init = get_api(mod, "_init");
mod->release = get_api(mod, "_release");
mod->signal = get_api(mod, "_signal");
static void *
_try_open(struct modules *m, const char * name) {
const char *l;
const char * path = m->path;
size_t path_size = strlen(path);
size_t name_size = strlen(name);
int sz = path_size + name_size;
//search path
void * dl = NULL;
char tmp[sz];
do
{
memset(tmp,0,sz);
while (*path == ';') path++;
if (*path == '\0') break;
l = strchr(path, ';');
if (l == NULL) l = path + strlen(path);
int len = l - path;
int i;
for (i=0;path[i]!='?' && i < len ;i++) {
tmp[i] = path[i];
}
memcpy(tmp+i,name,name_size);
if (path[i] == '?') {
strncpy(tmp+i+name_size,path+i+1,len - i - 1);
} else {
fprintf(stderr,"Invalid C service path\n");
exit(1);
}
dl = dlopen(tmp, RTLD_NOW | RTLD_GLOBAL);
path = l;
}while(dl == NULL);
if (dl == NULL) {
fprintf(stderr, "try open %s failed : %s\n",name,dlerror());
}
return dl;
}
static struct skynet_module *
_query(const char * name) {
int i;
for (i=0;i<M->count;i++) {
if (strcmp(M->m[i].name,name)==0) {
return &M->m[i];
}
}
return NULL;
}
static void *
get_api(struct skynet_module *mod, const char *api_name) {
size_t name_size = strlen(mod->name);
size_t api_size = strlen(api_name);
char tmp[name_size + api_size + 1];
memcpy(tmp, mod->name, name_size);
memcpy(tmp+name_size, api_name, api_size+1);
char *ptr = strrchr(tmp, '.');
if (ptr == NULL) {
ptr = tmp;
} else {
ptr = ptr + 1;
}
return dlsym(mod->module, ptr);
}
static int
open_sym(struct skynet_module *mod) {
mod->create = get_api(mod, "_create");
mod->init = get_api(mod, "_init");
mod->release = get_api(mod, "_release");
mod->signal = get_api(mod, "_signal");
return mod->init == NULL;
}
struct skynet_module *
skynet_module_query(const char * name) {
struct skynet_module * result = _query(name);
if (result)
return result;
SPIN_LOCK(M)
result = _query(name); // double check
if (result == NULL && M->count < MAX_MODULE_TYPE) {
int index = M->count;
void * dl = _try_open(M,name);
if (dl) {
M->m[index].name = name;
M->m[index].module = dl;
if (open_sym(&M->m[index]) == 0) {
M->m[index].name = skynet_strdup(name);
M->count ++;
result = &M->m[index];
}
}
}
SPIN_UNLOCK(M)
return result;
}
延伸
skynet新建一个context时,context都要去创建使用一个服务模块,并且调用skynet_module_instance_create和skynet_module_instance_init,模板服务init函数通常会调用skynet_callback绑定一个回调函数给context,用于消息回调处理,例如logger服务。
int
logger_init(struct logger * inst, struct skynet_context *ctx, const char * parm) {
const char * r = skynet_command(ctx, "STARTTIME", NULL);
inst->starttime = strtoul(r, NULL, 10);
if (parm) {
inst->handle = fopen(parm,"a");
if (inst->handle == NULL) {
return 1;
}
inst->filename = skynet_malloc(strlen(parm)+1);
strcpy(inst->filename, parm);
inst->close = 1;
} else {
inst->handle = stdout;
}
if (inst->handle) {
skynet_callback(ctx, inst, logger_cb);
return 0;
}
return 1;
}