文前导读
skynet 是一个由云风所写的轻量级在线游戏服务器框架。本文为 skynet 框架源码剖析系列的第五篇文章,探讨了 skynet 的引导服务 bootstrap 的实现,主要包含了以下内容:
- bootstrap 如何启动,属于何种服务
- snlua 服务的加载及初始化
- bootstrap 的主要作用
引导服务 bootstrap 的启动
skynet 在启动的初期,在 skynet_start
函数中创建了两个服务 logger
和 bootstrap
。其中 bootstrap
是一个 snlua 类型的服务,主要通过 bootstrap
函数来实现:
1 | //syknet_start.c |
可以看到,在上述代码中 bootstrap
主要的工作便是调用 skynet_context_new
创建了一个名为 snlua
的服务。我们来看下 skynet_context_new
的代码实现:
1 | struct skynet_context* skynet_context_new(const char * name, const char *param) { |
snlua 的加载及初始化
从前面 skynet_context_new
函数中,我们可以看出 snlua
服务的启动需要利用到 skynet_module_instance_create
函数进行实例的创建,利用skynet_module_instance_init
函数进行初始化,这两个函数最终会调用到对应模块中的 *_create 和 *_init 函数。对于 snlua 模块而言,其对应代码保存在 service-src/service_snlua.c 文件中,最终会编译成为 snlua.so 文件。由于在前面的文章 skynet 源码阅读笔记 —— skynet 的模块与服务 中已经说明了模块加载的详细方式,因此这里不多着笔墨说明。我们先来看看 snlua 的基本数据结构,然后直接看相关的模块函数
1 | //service-src/service_snlua.c |
从上述代码中可知,snlua_create
会负责初始化 snlua
结构体,并将其返回,而 snlua_init
函数则负责将创建好的 snlua 服务的回调函数设置为 launch_cb
函数,并对其发送一个注册命令,完成后向 snlua 服务的次级消息队列发送一条消息。
skynet 为每个模块都提供了一组相应的命令,其对应的数据类型为 command_func
,skynet 为模块所提供的所有命令都存放在了 cmd_funcs
数组当中
1 | //skynet_service.c |
在了解了 command_func
的定义后,我们来看看 skynet_command
函数的实现
1 | //查找相应的命令,并返回命令函数的执行结果 |
从上述代码中,我们可以简单地总结一下 snlua 的启动流程:
- skynet 调用 bootstrap 函数创建了一个 snlua 服务
- 在 bootstrap 创建服务的过程中,会先从全局的 modules 中查找 snlua 模块是否已加载,如果没有则加载到内存当中。
- 加载完毕后,先调用 snlua_create 函数分配一个 snlua 结构体,该结构体中包含了一个独立的 lua 运行状态,用于执行相应的 lua 脚本
- 创建好对应的 snlua 模块实例后,执行 snlua_init 函数为其进行初始化。初始化的过程中负责设置服务的回调函数,并向 snlua 服务发送一个注册命令,随后向 snlua 服务发送一条消息
- 将 snlua 的消息队列压入全局的消息队列当中
完成上述的 5 个步骤后,一个 snlua 服务就算是启动起来了。
bootstrap 服务的主要工作
在前面的内容当中,我们看到了 snlua 模块在初始化的过程当中会向自己发送一条消息,这样做的目的是为了自身的服务启动起来。因为在 skynet 当中,服务要依靠消息来驱动。snlua 在初始化过程当中向自身发送了一条消息,当 snlua 服务创建完毕后,worker 线程便会消息队列当中取出消息并执行相应的回调函数 launch_cb
函数进行消费,这样就能够将 snlua 服务运转起来。我们来看一下 launch_cb
的实现:
1 | //msg 的值为 bootstrap |
从上述代码中可以看出,bootstrap服务(即前面的 snlua 服务)在触发时,会调用 init_cb
来代替 lauch_cb
函数。简单地来说,init_cb
中最主要的部分便是设置相应的环境变量以及加载器loader。其中,环境变量的意义如下:
LUA_PATH:Lua搜索路径,在config.lua_path指定。
LUA_CPATH:C模块的搜索路径,在config.lua_cpath指定。
LUA_SERVICE:Lua服务的搜索路径,在config.luaservice指定。
LUA_PRELOAD:预加载脚本,这些脚本会在所有服务开始之前执行,可以用它来初始化一些全局的设置。
在设置好相应的环境变量后,init_cb
会执行 loader.lua,并将 bootstrap.lua 传进去。loader.lua 的主要作用是对环境变量以及传入的参数进行一些文本处理,然后找到对应的文件去执行,这里的参数主要是指 bootstrap
,最终会执行 /service/bootstrap.lua 文件。其中 bootstrap.lua 的源码如下:
1 | --将 skynet.lua 中定义的函数引用到当前文件 |
从上述 lua 代码中,我们可以看出 bootstrap.lua 的主要工作如下:
- 启动
launcher
服务,这个服务是一个通用的服务启动器,如果我们需要在lua创建一个 C 服务就需要用到它- 启动
datacenterd
服务- 启动
service_mgr
服务- 根据 config 中的 start 字段,指定相应的 lua 脚本,在 bootstrap 服务中启动的是 main.lua 脚本
到目前为止,bootstrap 服务的基本内容大概就说完了,而相关的一些其他一部分未说明清楚的部分(如main.lua, skynet.newservice, skynet_launch 等)则留在其他文章中讨论
- 本文作者: Phoenix
- 本文链接: http://hacker-cube.com/2020/11/04/skynet-源码阅读笔记-——-引导服务-bootstrap-的启动/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!