skynet 启动c服务的过程

本文主要参考&转载:skynet源码赏析

云风的 BLOG: skynet Archives

云风的 BLOG: Skynet 设计综述

本文旨在记录我对skynet重新学习和理解的过程,也便于以后回顾(本文纯手打,输出的过程也是记忆的过程)。

创建一个c服务,一般要经历下面几个步骤:

  1. 从modules列表中,查找对应的服务模块,如果找到则返回,否则到modules的path中去查找对应的so库,创建一个skynet_module对象(数据结构见上节),将so库加载到内存,并将访问该so库的句柄和skynet_module对象关联(_try_open做了这件事),并将so库中的xxx_create,xxx_init,xxx_signal,xxx_release四个函数地址赋值给skynet_module的create、init、signal和release四个函数中,这样这个skynet_module对象,就能调用so库中,对应的四个接口(_open_sym做了这件事)。
  2. 创建一个服务实例即skynet_context对象,他包含一个次级消息队列指针,服务模块指针(skynet_module对象,便于他访问module自定义的create、init、signal和release函数),由服务模块调用create接口创建的数据实例等。
  3. 将新创建的服务实例(skynet_context对象)注册到全局的服务列表中(见上节的handle_storage结构)。
  4. 初始化服务模块(skynet_module创建的数据实例),并在初始化函数中,注册新创建的skynet_context实例的callback函数
  5. 将该服务实例(skynet_context实例)的次级消息队列,插入到全局消息队列中。
    经过上面的步骤,一个c服务模块就被创建出来了,在回调函数被指定以后,其他服务发送给他的消息,会被pop出来,最终传给服务对应的callback函数,最后达到驱动服务的目的。

以创建logger服务为例子:

  1. 启动skynet节点时,会启动logger服务 

    // skynet_start.c
    skynet_start(struct skynet_config * config) {
        ...
        struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);
        if (ctx == NULL) {
    	    fprintf(stderr, "Can't launch %s service\n", config->logservice);
    	    exit(1);
        }
        ...
    }

    config->logservice指log服务的so库名称,用于后面加载服务时使用,config->logger指log的输出路径。

  2. 在modules.skynet_module列表中查找logger服务模块,如果没找到则在so库中寻找,通过dlopen函数获得logger服务部模块的句柄,通过dlsym函数将其内的logger_create、logger_init、logger_release三个函数的地址赋值给logger模块中的create、init、release函数指针。
  3. 通过logger模块的create函数创建服务实例
    // service_logger.c
    struct logger {
    	FILE * handle;
    	char * filename;
    	uint32_t starttime;
    	int close;
    };
    
    struct logger *
    logger_create(void) {
    	struct logger * inst = skynet_malloc(sizeof(*inst));
    	inst->handle = NULL;
    	inst->close = 0;
    	inst->filename = NULL;
    
    	return inst;
    }
    在skynet_context_new函数会malloc一个skynet_context,将logger服务实例赋值给skynet_context的instance变量。
  4. 为了使skynet_context能访问logger服务内部函数,将logger模块指针赋值给skynet_context中的mod变量。此时一个服务对象所要用到的逻辑能够通过mod变量访问logger服务对应的函数,而通过instance可以找到服务自身的数据块。
  5. 将新建的skynet_context加入到skynet_context列表中。
  6. 创建服务对应message_queue消息队列
  7. 初始化logger服务,注册logger服务的callback函数
    // service_logger.c
    static int
    logger_cb(struct skynet_context * context, void *ud, int type, int session, uint32_t source, const void * msg, size_t sz) {
    	struct logger * inst = ud;
    	switch (type) {
    	case PTYPE_SYSTEM:
    		if (inst->filename) {
    			inst->handle = freopen(inst->filename, "a", inst->handle);
    		}
    		break;
    	case PTYPE_TEXT:
    		if (inst->filename) {
    			char tmp[SIZETIMEFMT];
    			int csec = timestring(ud, tmp);
    			fprintf(inst->handle, "%s.%02d ", tmp, csec);
    		}
    		fprintf(inst->handle, "[:%08x] ", source);
    		fwrite(msg, sz , 1, inst->handle);
    		fprintf(inst->handle, "\n");
    		fflush(inst->handle);
    		break;
    	}
    
    	return 0;
    }
    
    int
    logger_init(struct logger * inst, struct skynet_context *ctx, const char * parm) {
    	...
    	if (inst->handle) {
    		skynet_callback(ctx, inst, logger_cb);
    		return 0;
    	}
    	return 1;
    }
    
    // skynet_server.c
    void 
    skynet_callback(struct skynet_context * context, void *ud, skynet_cb cb) {
    	context->cb = cb;
    	context->cb_ud = ud;
    }
    
  8. 将logger服务的次级消息队列加入到global_queue全局消息队列中

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值