博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
skynet启动过程_1
阅读量:5096 次
发布时间:2019-06-13

本文共 6397 字,大约阅读时间需要 21 分钟。

skynet的启动时需带个配置文件,这个文件其实是作为lua全局变量用的,见
 
intmain(int argc, char *argv[]) {     const char * config_file = NULL ;     if (argc > 1) {          config_file = argv[1];     } else {          fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"               "usage: skynet configfilename\n");          return 1;     }     skynet_globalinit();     skynet_env_init();     sigign();     struct skynet_config config;     struct lua_State *L = lua_newstate(skynet_lalloc, NULL);     luaL_openlibs(L);     // link lua lib     int err = luaL_loadstring(L, load_config);     assert(err == LUA_OK);     lua_pushstring(L, config_file);         err = lua_pcall(L, 1, 1, 0);     if (err) {          fprintf(stderr,"%s\n",lua_tostring(L,-1));          lua_close(L);          return 1;     }     _init_env(L);     config.thread =  optint("thread",8);     config.module_path = optstring("cpath","./cservice/?.so");     config.harbor = optint("harbor", 1);     config.bootstrap = optstring("bootstrap","snlua bootstrap");     config.daemon = optstring("daemon", NULL);     config.logger = optstring("logger", NULL);     lua_close(L);     skynet_start(&config);     skynet_globalexit();     return 0;}

配置了一些基本的环境变量后,转到skynet_start方法,开始启动skynet,在skynet_start方法中初始化一些变量后,系统启动的第一个服务是logger:

struct skynet_context *ctx = skynet_context_new("logger", config->logger);     if (ctx == NULL) {          fprintf(stderr, "Can't launch logger service\n");          exit(1);     }
skynet通过skynet_context_new函数来实例化一个服务:先是从logger.so文件把模块加载进来;
 
struct skynet_module * mod = skynet_module_query(name);     if (mod == NULL)          return NULL;
让模块自生成一个新的实例;
 
void *inst = skynet_module_instance_create(mod);
分配一个新的handle;
 
ctx->handle = skynet_handle_register(ctx);
初始化一个消息队列;
 
struct message_queue * queue = ctx->queue = skynet_mq_create(ctx->handle);
调用这个模块的初始化方法
 
int r = skynet_module_instance_init(mod, inst, ctx, param);
最后是把自己的消息队列加入到全局消息队列中,把有加入到全局的消息队列后,才能收到消息回调
 
skynet_globalmq_push(queue);

启动完成logger服务后,系统接下来要启动的服务是bootstrap,但先要加载snlua模块,所有的lua服务都属于snlua模块的实例。

static voidbootstrap(struct skynet_context * logger, const char * cmdline) {     int sz = strlen(cmdline);     char name[sz+1];     char args[sz+1];     sscanf(cmdline, "%s %s", name, args);     struct skynet_context *ctx = skynet_context_new(name, args);     if (ctx == NULL) {          skynet_error(NULL, "Bootstrap error : %s\n", cmdline);          skynet_context_dispatchall(logger);          exit(1);     }}
其中参数cmdline是在config配置里的
bootstrap = "snlua bootstrap"   
 
和加载logger服务类似,先是把snlua.so文件作为模块加载进来,调用模块自身的_create函数产生一个snlua实例,在service_snlua.c文件中。
struct snlua *snlua_create(void) {     struct snlua * l = skynet_malloc(sizeof(*l));     memset(l,0,sizeof(*l));     l->L = lua_newstate(skynet_lalloc, NULL);     return l;}
在方法中启动了新生成了一个lua VM,出就是lua沙盒环境,这一点也比较重要,因为所有的lua服务都是是一个独立的VM中运行的,这也是云风的设计初衷。
 
接下来就会调用了service_snlua.c中的snlua_init方法
intsnlua_init(struct snlua *l, struct skynet_context *ctx, const char * args) {     int sz = strlen(args);     char * tmp = skynet_malloc(sz);     memcpy(tmp, args, sz);     skynet_callback(ctx, l , _launch);     const char * self = skynet_command(ctx, "REG", NULL);     uint32_t handle_id = strtoul(self+1, NULL, 16);     // it must be first message     skynet_send(ctx, 0, handle_id, PTYPE_TAG_DONTCOPY,0, tmp, sz);     return 0;}
来初始化服务,在这个方法中做了两件事情;
注册了一个回调函数,当有消息到来时,这个函数会被调用
skynet_callback(ctx, l , _launch);
向自己发送了一条消息,并附带了一个参数,这个参数就是bootstrap。当把消息队列加入到全局队列后,收到的第一条消息就是这条消息。
收到第一条消息后,调用到callback函数,也就是service_snlua.c里的_launch方法
 
static int_launch(struct skynet_context * context, void *ud, int type, int session, uint32_t source , const void * msg, size_t sz) {     assert(type == 0 && session == 0);     struct snlua *l = ud;     skynet_callback(context, NULL, NULL);     int err = _init(l, context, msg, sz);     if (err) {          skynet_command(context, "EXIT", NULL);     }     return 0;}
这个方法里把自己的回调函数给注销了,使它不再接收消息,为的是在lua层重新注册它,把消息通过lua接口来接收。紧接着执行_init方法。在_init方法里设置了一些虚拟机环境变量后,就加载执行了loader.lua文件,同时要把真正要加载的文件(这个时候是bootstrap)作为参数传给它, 控制权就开始转到lua层。
loader.lua是用来加载lua文件的,在loader.lua中会判断是否需要preload,最终会加载执行bootstrap.lua文件:
local skynet = require "skynet"local harbor = require "skynet.harbor"skynet.start(function()     local standalone = skynet.getenv "standalone"     local launcher = assert(skynet.launch("snlua","launcher"))     skynet.name(".launcher", launcher)     local harbor_id = tonumber(skynet.getenv "harbor")     if harbor_id == 0 then          assert(standalone ==  nil)          standalone = true          skynet.setenv("standalone", "true")          local ok, slave = pcall(skynet.newservice, "cdummy")          if not ok then               skynet.abort()          end          skynet.name(".cslave", slave)     else          if standalone then               if not pcall(skynet.newservice,"cmaster") then                    skynet.abort()               end          end          local ok, slave = pcall(skynet.newservice, "cslave")          if not ok then               skynet.abort()          end          skynet.name(".cslave", slave)     end     if standalone then          local datacenter = skynet.newservice "datacenterd"          skynet.name("DATACENTER", datacenter)     end     skynet.newservice "service_mgr"     pcall(skynet.newservice,skynet.getenv "start" or "main")     skynet.exit()end)

在这个文件里启动了其它一些服务,这些暂不看,在这个文件里调用了服务启动的接口skynet.start。这也是所有lua服务的标准启动入口,参数是一个回调方法,服务启动完成后会调到这个方法。做一些初始化的工作。

skynet.lua文件的start方法:
 
function skynet.start(start_func)     c.callback(dispatch_message)     skynet.timeout(0, function()          init_service(start_func)     end)end
通过
c.callback(dispatch_message)
重新注册了callback函数,这样就能在lua接收消息了。收到消息时,通过dispatch_message方法来分发。
c.callback调用的是一个c函数,在lua-skynet.c文件的_callback方法。
static int_callback(lua_State *L) {     struct skynet_context * context = lua_touserdata(L, lua_upvalueindex(1));     int forward = lua_toboolean(L, 2);     luaL_checktype(L,1,LUA_TFUNCTION);     lua_settop(L,1);     lua_rawsetp(L, LUA_REGISTRYINDEX, _cb);     lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);     lua_State *gL = lua_tothread(L,-1);     if (forward) {          skynet_callback(context, gL, forward_cb);     } else {          skynet_callback(context, gL, _cb);     }     return 0;}
在这个方法中可以看到,重新调用了skynet_callback来注册服务的回调函数。
到此,一个lua编写的服务就启动起来了。

 

 

转载于:https://www.cnblogs.com/lycokcc/p/4277113.html

你可能感兴趣的文章
urllib.unquote()、urllib.urlencode()
查看>>
JSP的学习(7)——九大隐式对象之pageContext对象
查看>>
.NET对象序列化—TimeSpan
查看>>
sprint 1 总结
查看>>
JS实现禁止短时间内连续触发事件
查看>>
最大连续子序列和问题(Maximum Consecutive Subsequence Sum)
查看>>
redis 的过期策略都有哪些?内存淘汰机制都有哪些?
查看>>
[转]友元函数(friend)
查看>>
Unity树木生成器
查看>>
jsp页面设置a标签失效
查看>>
hdu1000
查看>>
poj3264
查看>>
Android 网络编程
查看>>
SpringBoot的文件上传
查看>>
几大主流浏览器的内核
查看>>
jbpm3.1.4设计器(eclipse3.1.4)中gpd.xml文件乱码的处理
查看>>
Java线程:线程状态的转换(转)
查看>>
视图以日期作为条件查询条件时虽显式转换?
查看>>
LintCode: Number of Islands
查看>>
git教程: 创建版本库
查看>>