skynet-skynet_module解析
- Makefile中的module相关
- skynet_module.h
- skynet_module.c
- skynet_context:
- skynet_dl_create:
- skynet_dl_init:
- skynet_dl_release:
- skynet_dl_signal:
- struct skynet_module:
- skynet_module_insert:
- skynet_module_query:
- skynet_module_init:
- struct modules:
- _try_open:
- _query:
- get_api:
- open_sym:
- 设计优势
Makefile中的module相关
源码:
define CSERVICE_TEMP
$$(CSERVICE_PATH)/$(1).so : service-src/service_$(1).c | $$(CSERVICE_PATH)
$$(CC) $$(CFLAGS) $$(SHARED) $$< -o $$@ -Iskynet-src
endef
解析:
这部分用来生成一个名为
(
1
)
.
s
o
的
动
态
链
接
库
文
件
,
其
中
(1).so 的动态链接库文件,其中
(1).so的动态链接库文件,其中(1) 是一个占位符,表示模块名。而模块的源文件是以 service_$(1).c 的形式命名的,意味着在 skynet 框架中定义 C 模块时,建议使用 service_ 作为文件名的前缀,以便于在 Makefile 中进行统一的编译和链接处理
skynet_module.h
skynet_module.h
中定义了 skynet 框架中用于管理模块的相关结构体和函数声明,框架可以灵活地扩展功能,实现高度的模块化。通过这种设计,可以更方便地将新的功能模块集成到 skynet 框架中,同时保证了模块之间的独立性和可管理性
源码:
#ifndef SKYNET_MODULE_H
#define SKYNET_MODULE_H
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;
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);
#endif
skynet_module.c
skynet_module.c
定义了模块管理器,负责动态加载和管理skynet框架中的模块
源码:
#include "skynet.h"
#include "skynet_module.h"
#include "spinlock.h"
#include <assert.h>
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#define MAX_MODULE_TYPE 32
struct modules {
int count;
struct spinlock lock;
const char * path;
struct skynet_module m[MAX_MODULE_TYPE];
};
static struct modules * M = NULL;
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;
}
void
skynet_module_insert(struct skynet_module *mod) {
SPIN_LOCK(M)
struct skynet_module * m = _query(mod->name);
assert(m == NULL && M->count < MAX_MODULE_TYPE);
int index = M->count;
M->m[index] = *mod;
++M->count;
SPIN_UNLOCK(M)
}
void *
skynet_module_instance_create(struct skynet_module *m) {
if (m->create) {
return m->create();
} else {
return (void *)(intptr_t)(~0);
}
}
int
skynet_module_instance_init(struct skynet_module *m, void * inst, struct skynet_context *ctx, const char * parm) {
return m->init(inst, ctx, parm);
}
void
skynet_module_instance_release(struct skynet_module *m, void *inst) {
if (m->release) {
m->release(inst);
}
}
void
skynet_module_instance_signal(struct skynet_module *m, void *inst, int signal) {
if (m->signal) {
m->signal(inst, signal);
}
}
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_context:
其中
skynet_context
的定义如下,struct skynet_context结构体用于表示一个skynet服务的上下文或环境。其包含了该服务的各种属性和状态信息,并提供了方法来管理和控制服务的行为
struct skynet_context {
void * instance;
struct skynet_module * mod;
void * cb_ud;
skynet_cb cb;
struct message_queue *queue;
ATOM_POINTER logfile;
uint64_t cpu_cost; // in microsec
uint64_t cpu_start; // in microsec
char result[32];
uint32_t handle;
int session_id;
ATOM_INT ref;
int message_count;
bool init;
bool endless;
bool profile;
CHECKCALLING_DECL
};
void *instance: 该字段通常用于存储与该skynet服务实例相关的数据或指针。
struct skynet_module *mod: 指向skynet模块的指针,用于与该服务相关联的模块管理。
void *cb_ud: 回调函数的用户数据,用于回调函数与服务的交互。
skynet_cb cb: 回调函数,用于处理服务接收到的消息或事件。
struct message_queue *queue: 消息队列指针,用于管理服务接收到的消息。
ATOM_POINTER logfile: 用于记录日志的原子指针。
uint64_t cpu_cost: 服务运行时的CPU消耗。
uint64_t cpu_start: 服务开始运行时的CPU时间。
char result[32]: 存储服务执行结果的缓冲区。
uint32_t handle: 服务的句柄,用于标识唯一的服务实例。
int session_id: 会话ID,用于标识与服务相关的会话。
ATOM_INT ref: 引用计数,用于管理服务的生命周期。
int message_count: 当前服务接收到的消息数量。
bool init: 标志位,表示服务是否已初始化。
bool endless: 标志位,表示服务是否是无限循环执行的。
bool profile: 标志位,表示是否开启性能分析。
skynet_dl_create:
- 调用模块的
create
函数创建模块的一个实例
skynet_dl_init:
- 调用模块的
init
初始化模块实例。参数包括实例对象指针、actor 上下文指针以及初始化参数
skynet_dl_release:
- 调用模块的
release
释放实例对象占用的资源
skynet_dl_signal:
- 调用模块的
signal
向模块实例发送信号。用于通知模块实例处理事件响应
struct skynet_module:
- 表示一个动态加载的actor模块
- 包含特定模块函数的函数指针 (
create
,init
,release
,signal
) - 存储模块的名称和加载的共享库的句柄
skynet_module_insert:
- 用于将一个模块插入到 skynet 框架中进行管理。
skynet_module_query:
- 从集合中按名称检索模块,返回相应的模块结构体指针
- 如果未找到,则尝试使用 _try_open 加载模块,如果成功则将其添加到集合中
skynet_module_init:
- 用于初始化模块(M),可以指定模块所在的路径
struct modules:
这个结构体用于存储模块的信息,包括模块数量、模块路径以及模块数组。使用自旋锁来保护模块数据的并发访问
- 管理一组已加载的模块。
- 使用自旋锁保证线程安全。
- 包含 skynet_module 结构的数组 (m)。
_try_open:
- 尝试从各个目录 (path) 加载一个模块 (name)
- 通过将目录路径中的 ? 替换为模块名称构建完整路径
- 使用 dlopen 加载模块
_query:
- 在已加载的模块集合 (M) 中按名称搜索模块
get_api:
- 在模块 (mod) 中构建函数名 (api_name) 并使用dlsym获取其地址
open_sym:
- 通过使用 get_api获取函数地址初始化模块的函数指针 (
create
,init
,release
,signal
)
设计优势
-
模块化设计: 通过将模块的创建、初始化、释放等功能封装在模块管理器中,实现了模块化设计,提高了代码的可维护性和可扩展性
-
并发安全: 使用自旋锁保护模块数据的并发访问,确保了在多线程环境下的安全性
-
支持动态链接和静态链接: 既支持动态链接的模块(通过
dlopen
加载),也支持静态链接的模块(通过skynet_module_insert
手动插入) -
易用性: 对外提供简洁的接口,使得使用者能够方便地加载、查询和操作模块