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编写的服务就启动起来了。

     风潇潇

  • 相关阅读:
    【JAVA笔记——道】JAVA对象销毁
    【JAVA笔记——道】并发编程CAS算法
    httpClientUtil的get请求
    python基础 day11 下 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业
    python基础 day11 上 数据库介绍 mysql 数据库安装使用 mysql管理 mysql 数据类型 常用mysql命令 事务 索引 python 操作mysql ORM sqlachemy学习
    Python基础 Day10 Gevent协程 SelectPollEpoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 RedisMemcached缓存 Paramiko SSH Twsited网络框架
    python基础 day9 进程、与线程区别 python GIL全局解释器锁 线程 进程
    python基础 day8 Socket语法及相关 SocketServer实现多并发
    python基础 day7 面向对象高级语法部分 异常处理 异常处理 Socket开发基础
    python基础 day6 面向对象的特性:封装、继承、多态 类、方法、
  • 原文地址:https://www.cnblogs.com/lycokcc/p/4277113.html
Copyright © 2011-2022 走看看