module初始化
skynet中每个C服务都是一个module。源码位于skynet_module.c在启动时设置module的默认查找路径为"./cservice/?.so",创建modules实例。
skynet_main.c
int
main(int argc, char *argv[]) {
....
config.module_path = optstring("cpath","./cservice/?.so");
....
skynet_start(&config);
}
skynet_start.c
void
skynet_start(struct skynet_config * config) {
....
skynet_module_init(config->module_path);
....
}
skynet_module.c
void
skynet_module_init(const char *path) {
struct modules *m = skynet_malloc(sizeof(*m));
m->count = 0;
m->path = skynet_strdup(path);
SPIN_INIT(m)
M = m;
}
基本数据结构:
skynet_module.c
#define MAX_MODULE_TYPE 32
struct modules {
int count; //已加载动态库数量
struct spinlock lock;
const char * path; //动态库查找路径
struct skynet_module m[MAX_MODULE_TYPE]; //已加载动态库数组
};
struct skynet_module {
const char * name; //动态库名
void * module; //动态库句柄
skynet_dl_create create; //动态库create函数地址
skynet_dl_init init; //动态库init函数地址
skynet_dl_release release; //动态库release函数地址
skynet_dl_signal signal; //动态库signal函数地址
};
跟踪logger 服务的创建流程:
void
skynet_start(struct skynet_config * config) {
....
struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
....
}
struct skynet_context *
skynet_context_new(const char * name, const char *param) {
struct skynet_module * mod = skynet_module_query(name);
....
}
struct skynet_module *
skynet_module_query(const char * name) {
//查找是动态库是否已经加载在modules缓存中
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;
//加载动态库 调用dlopen
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) {
//获取dlsym 动态库函数地址
M->m[index].name = skynet_strdup(name);
M->count ++;
result = &M->m[index];
}
}
}
SPIN_UNLOCK(M)
return result;
}
创建logger C服务首先查找logger.so 是否已经加载在modules缓存中。已经加载直接返回logger.so的skynet_module对象。否则加载logger.so,保存create、init等函数的地址。添加到modules中。
skynet_module 返回后,再分别用缓存的logger.so中的create init函数指针,执行动态库中的create init函数。
struct skynet_context *
skynet_context_new(const char * name, const char *param) {
...
void *inst = skynet_module_instance_create(mod);
int r = skynet_module_instance_init(mod, inst, ctx, param);
...
}
动态库函数的命名规则:modulename_funcname
XXX_create:创建实例
XXX_init:初始实例,设置实例的回调函数
XXX_release:释放实例
在lua中通过skynet.launch加载C服务。
总结:module就是一个动态库,动态库中定义了用户自定义的逻辑数据结构,和数据结构的处理函数。通过dlopen加载动态库,dlsym 获取动态库中约定的函数指针。然后通过函数指针执行动态库中的create函数创建数据结构和init初始化函数。当底层向服务投递消息是,通过在init中设置的回调来进行处理。