skynet学习笔记 源码之sknyet_module模块

前言

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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值