zoukankan      html  css  js  c++  java
  • skynet启动过程_1

    skynet的启动时需带个配置文件,这个文件其实是作为lua全局变量用的,见
     
    int
    main(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
    "
                   "usage: skynet configfilename
    ");
              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
    ",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
    ");
              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 void
    bootstrap(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
    ", 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方法
    int
    snlua_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编写的服务就启动起来了。

     风潇潇

  • 相关阅读:
    Servlet与JSP转发与包含---forwardinclude
    爬虫---正则表达式
    MapReduce的核心资料索引
    设计模式之单例模式Singleton pattern
    HTTP会话的使用与管理
    (二)jquery学习----jquery的效果
    (一)jquery学习
    webStorm2017 安装及使用
    linux下的符号链接和硬链接
    <初级程序员> git 的初级使用
  • 原文地址:https://www.cnblogs.com/lycokcc/p/4277113.html
Copyright © 2011-2022 走看看