skynet的启动时需带个配置文件,这个文件其实是作为lua全局变量用的,见

int
main(int argc, char *argv[]) {
const char * config_file = NULL ;
if (argc > ) {
config_file = argv[];
} else {
fprintf(stderr, "Need a config file. Please read skynet wiki : https://github.com/cloudwu/skynet/wiki/Config\n"
"usage: skynet configfilename\n");
return ;
}
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, , , );
if (err) {
fprintf(stderr,"%s\n",lua_tostring(L,-));
lua_close(L);
return ;
}
_init_env(L); config.thread = optint("thread",);
config.module_path = optstring("cpath","./cservice/?.so");
config.harbor = optint("harbor", );
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 ;
}

配置了一些基本的环境变量后,转到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();
}
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+];
char args[sz+];
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();
}
}
其中参数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,,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+, NULL, );
// it must be first message
skynet_send(ctx, , handle_id, PTYPE_TAG_DONTCOPY,, tmp, sz);
return ;
}
来初始化服务,在这个方法中做了两件事情;
注册了一个回调函数,当有消息到来时,这个函数会被调用
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 == && session == );
struct snlua *l = ud;
skynet_callback(context, NULL, NULL);
int err = _init(l, context, msg, sz);
if (err) {
skynet_command(context, "EXIT", NULL);
} return ;
}
这个方法里把自己的回调函数给注销了,使它不再接收消息,为的是在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 == 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(, 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());
int forward = lua_toboolean(L, );
luaL_checktype(L,,LUA_TFUNCTION);
lua_settop(L,);
lua_rawsetp(L, LUA_REGISTRYINDEX, _cb); lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
lua_State *gL = lua_tothread(L,-); if (forward) {
skynet_callback(context, gL, forward_cb);
} else {
skynet_callback(context, gL, _cb);
} return ;
}
在这个方法中可以看到,重新调用了skynet_callback来注册服务的回调函数。
到此,一个lua编写的服务就启动起来了。

风潇潇

skynet启动过程_1的更多相关文章

  1. skynet启动过程_bootstrap

    这遍摘自skynet 的wiki skynet 由一个或多个进程构成,每个进程被称为一个 skynet 节点.本文描述了 skynet 节点的启动流程. skynet 节点通过运行 skynet 主程 ...

  2. zookeeper源码分析之一服务端启动过程

    zookeeper简介 zookeeper是为分布式应用提供分布式协作服务的开源软件.它提供了一组简单的原子操作,分布式应用可以基于这些原子操作来实现更高层次的同步服务,配置维护,组管理和命名.zoo ...

  3. [原] KVM 虚拟化原理探究(2)— QEMU启动过程

    KVM 虚拟化原理探究- QEMU启动过程 标签(空格分隔): KVM [TOC] 虚拟机启动过程 第一步,获取到kvm句柄 kvmfd = open("/dev/kvm", O_ ...

  4. Openfire的启动过程与session管理

    说明   本文源码基于Openfire4.0.2.   Openfire的启动       Openfire的启动过程非常的简单,通过一个入口初始化lib目录下的openfire.jar包,并启动一个 ...

  5. 探索 Linux 系统的启动过程

    引言 之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉.我是真的在日常生活和工作中都使用 Linux,比如在 ...

  6. Linux内核启动过程概述

    版权声明:本文原创,转载需声明作者ID和原文链接地址. Hi!大家好,我是CrazyCatJack.今天给大家带来的是Linux内核启动过程概述.希望能够帮助大家更好的理解Linux内核的启动,并且创 ...

  7. SpringMVC启动过程详解(li)

    通过对SpringMVC启动过程的深入研究,期望掌握Java Web容器启动过程:掌握SpringMVC启动过程:了解SpringMVC的配置文件如何配置,为什么要这样配置:掌握SpringMVC是如 ...

  8. Android源码——Activity组件的启动过程

    根Activity启动过程 Launcher启动MainActivity的过程主要分为6个步骤: 一.Launcher向ActivityManagerService发送一个启动MainActivity ...

  9. Netty源码分析之客户端启动过程

    一.先来看一下客户端示例代码. public class NettyClientTest { public void connect(int port, String host) throws Exc ...

随机推荐

  1. POI导出数据内存溢出问题

    POI之前的版本不支持大数据量处理,如果数据过多则经常报OOM错误,有时候调整JVM大小效果也不是太好.3.8版本的POI新出来了SXSSFWorkbook,可以支持大数据量的操作,只是SXSSFWo ...

  2. ASP.NET 導入Excel

    常常碰到這種需求,為了避免自己每次寫Code都要東翻西找Sample,乾脆丟上來當備份 此外,也為了方便網路上的大大們Copy Paste方便,小弟已經順便標示要複製程式碼的起始結束位置 在歡樂的貼程 ...

  3. C#程序中访问配置文件

    在C#编程中,有时候会用到配置文件,那么该如何在程序中获取或修改配置文件中的相关数据呢?下面采用一个简单的C#控制台程序来说明. 新建一个C#控制台程序,打开“解决方案资源管理器”,如下图: 可以看到 ...

  4. 深刻理解C#的传值调用和传引用调用

    传值调用和传引用调用是几乎所有主流语言都会涉及到的问题,下面我谈谈我对C#中传值调用和传引用调用的理解. 1. 一般对C#中传值调用和传引用调用的理解 如果传递的参数是基元类型(int,float等) ...

  5. Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞

    漏洞名称: Linux Kernel‘ieee80211_radiotap_iterator_init()’函数拒绝服务漏洞 CNNVD编号: CNNVD-201312-041 发布时间: 2013- ...

  6. 白书P60 - 硬币问题

    白书P60 - 硬币问题 完全背包.DP #include <iostream> #include <cstdio> #include <cstring> usin ...

  7. easyui datagriad 框架 自适应r

    easyui datagriad 框架 在做列自适应时可以采取以下步骤: 1. fitColumns: true,/*自动扩大或缩小列的尺寸以适应表格的宽度并且防止水平滚动*/ 使之保持与父类宽度相同 ...

  8. 详解强大的SQL注入工具——SQLMAP

    1. 前言  Windows下的注入工具好的又贵,免费的啊D.明小子等又不好用,我们根本没必要花 时间去找什么破解的havij.pangolin什么的,特别是破解的工具很可能被绑了木马.其实 Linu ...

  9. C# 深复制

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Cons ...

  10. lua 中pairs 和 ipairs区别

    lua 中pairs 和 ipairs区别 标准库提供了集中迭代器,包括迭代文件每行的(io.lines),迭代table元素的(pairs),迭代数组元素的(ipairs),迭代字符串中单词的 (s ...