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. BZOJ_3207_花神的嘲讽计划1_(Hash+主席树)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3207 给出一个长度为\(n\)的串,以及\(m\)个长度为\(k\)的串,求每个长度为\(k\ ...

  2. WordPress 3.5.1远程代码执行漏洞

    漏洞版本: WordPress 3.5.1 漏洞描述: WordPress是一种使用PHP语言开发的博客平台,用户可以在支持PHP和MySQL 数据库的服务器上架设自己的网志.也可以把 WordPre ...

  3. easyui datagriad 框架 自适应r

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

  4. 【转】MFC中用CFile读取和写入文件2

    原文网址:http://blog.sina.com.cn/s/blog_623a7fa40100hh1u.html CFile提供了一些常用的操作函数,如表1-2所示. 表1-2  CFile操作函数 ...

  5. InstallShield2008脚本安装制作方法Setup

    //=========================================================================== // //  File Name:    S ...

  6. POJ2492 A Bug's Life 带权并查集

    分析:所谓带权并查集,就是比朴素的并查集多了一个数组,记录一些东西,例如到根的距离,或者和根的关系等 这个题,权数组为relation 代表的关系  1 和父节点不同性别,0,和父节点同性别 并查集一 ...

  7. 创建二叉树,C语言实现

    一.前序遍历创建二叉树,使用递归,头文件 BiTree.h /*槽点一:创建树时用scanf输入不成功*/ #ifndef BITREE_H #define BITREE_H #include< ...

  8. Robocopy是微软Windows Server 2003资源工具包中众多多用途的实用程序之一(它是基于强大的拷贝程序

    Robocopy是微软Windows Server 2003资源工具包中众多多用途的实用程序之一(它是基于强大的拷贝程序).没错,Robocopy的功能是拷贝文件,你也许会觉得无聊并且要翻阅下一篇文章 ...

  9. poj 1438--One-way Traffic(边的双连通)

    给定一个图,并给定边,a b c(c==1||c==2) 表示ab之间有c条边 求把尽可能多的有向边定向变成强联通图. 先把图当做无向图,加边时记录是否有边,dfs的时候不要把本没有的边用到!因为这个 ...

  10. 安装Go语言开发环境

    安装Go语言开发环境实例代码 - 详述Go语言安装所在需要的工作:安装C语言工具,安装Mercurial,更新go到新版本等操作实例. 安装go环境 1.简介 Go是一个开源项目,采用BSD授权协议. ...