skynet 源码分析-启动流程之创建service
Last updated
Last updated
书接上文, 在加载了各种配置, 初始化全局变量(handle_storage , global_queue, env, module, timer, epoll_socket) 之后, 接下来就要创建实际的service, 本文将详细讲述skynet 创建service 的流程
此处以logger 模块为例
在 query 时会在初始化的全局module 里寻找模块是否被加载, 此时还没有加载任何模块, 就会走到下一步的 _try_open 里去, 在 dlopen 打开动态链接库, 最后通过函数名获取对应模块的函数指针, 函数名分别为 logger_create, logger_init, logger_release, logger_signal
这里已经加载了动态链接库, 就会调用 logger模块 logger_create() 方法, 返回一个 logger的实例, 其他的service, harbor snlua 模块的启动都是这个流程
之后创建 skynet_context 对象, 把 mod 和 inst 与context 绑定, skynet_handle_register ctx 向handle_storage 上注册一个handle, skynet_mq_create 给ctx 创建自己的message_queue
skynet_module_instance_init 会调用logger 模块的 logger_init 方法, 在配置文件里我们没有写日志文件的路径, 所以 inst->handle = stdout , 其日志会打印到stdout, 最后给 ctx 设置回调函数 logger_cb, 到这里服务创建完成了, 最后把message_queue 放入全局的 global_queue 里
整个skynet_context_new 过程, 加载了mod, 创建mod 实例, 向全局的handle 注册一个 handle, 将 ctx 的 queue加入到全局队列
bootstrap 里也是 skynet_context_new 方法, 而这里先对配置文件里的 bootstrap 字符串做了分割, 分割后name=snlua, args=bootstrap, 流程一样, 去 service_snlua 模块里找对应的snlua_create 和 snlua_init 函数执行
snlua 在init 时, 做了两件比较重要的事, 第474行注册了callback 函数 launch_cb, 然后在475行获取了自己的harbor_id, 在478行给自己发送了一条msg(bootstrap), 这时会调用 launch_cb
init_cb前半段是加载lua 相应的环境, 423行添加trace 作为错误处理函数压入栈中, 428行加载 loader.lua 文件, 434行把args(bootstrap) 压入栈中, 435行执行 loader.lua 脚本, 把bootstrap 作为脚本的参数传入
在loader.lua 脚本中, 上半部分是做字符串操作, 我们这里传入的是bootstrap, service_name 就是 bootstrap, 13行 loadfile() 之后, main = f 让main 指向了 bootstrap.lua; 之后寻找service_path, 加载 preload(配置里没有), 最后执行 bootstrap.lua 脚本, 这里 select(2, table.unpack(args)), args只有一个参数bootstrap, 取args[2] 为空, 所以就是无参执行 bootstrap.lua 脚本
这里面 skynet.launch, skynet.xxx 等方法在后续会讲述是如何与 c 代码交互, 先搞明白bootstrap.lua 在做什么
在bootstrap.lua 中, 首先启动了launcher.lua 脚本, 用 harbor_id 区分单机模式还是集群模式, 单机模式下会走到 16 行启动 cdummy 服务, 之后启动 service_mgr 和 datacenterd 服务
最后 pcall(skynet.newservice,skynet.getenv "start" or "main") 这一步, 就去执行我们的 main.lua 脚本, 至此bootstrap 就完成了