Bootstrap

skynet-skynet_module解析

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)

设计优势

  1. 模块化设计: 通过将模块的创建、初始化、释放等功能封装在模块管理器中,实现了模块化设计,提高了代码的可维护性和可扩展性

  2. 并发安全: 使用自旋锁保护模块数据的并发访问,确保了在多线程环境下的安全性

  3. 支持动态链接和静态链接: 既支持动态链接的模块(通过dlopen加载),也支持静态链接的模块(通过skynet_module_insert手动插入)

  4. 易用性: 对外提供简洁的接口,使得使用者能够方便地加载、查询和操作模块

;