对于你不了解的框架或者引擎,介绍再多的逻辑结构都好像有点茫然的感觉。所以小编认为,最有效的方式就是搞清楚框架启动流程的步骤,让自己心中有一条线可以牵引着。
当你在终端输入./skeynet example/config的时候,就开始启动skynet了。那么skynet是从哪个文件的哪个函数开始运行呢?这里如果大家不知道的话可以用gdb的方式在main函数中打断点,像下面这样
b main
因为任何c/c++的进程都是main函数这个入口开始的,所以我们可以知道skynet框架是从skynet_main.c这个文件的main函数开始的。
以下内容转载于网友分享的文档里说明。对小编来说,如获珍宝,在这里也分享给大家。
启动流程的相关源代码在skynet-src\skynet_main.c 和skynet-src\skynet_start.c 这两个文件中。
skynet_main.c 主要是设置环境和加载配置文件,最后调用skynet_start.c 文件中的 skynet_start() 函数启动 Skynet 服务程序。
skynet_start.c 主要是初始化 Skynet 的各个组件,并创建监视线程、定时器线程、网络线程和工作线程。
一、skynet_main.c 的流程如下:
1、 从 main()入口函数开始。
2、 skynet_globalinit() 全局初始化,这个函数在 skynet_server.c中定义,主要功能是为线程特有数据创建一个 Key。使用pthread_key_create()函数。并使用 pthread_setspecific()函数设置特有数据Key的Value 值。
3、 skynet_env_init() 为初始化 Lua 的环境,这个函数在skynet_env.c 中定义,主要创建一个全局的数据结构 structskynet_env *E,并初始化结构的值。
4、 sigign()为信号处理,主要是用于忽略 SIGPIPE 信号的处理。
5、 从步骤 5 到步骤 11 主要是在 Lua 状态机中运行,lua_newstate(skynet_lalloc, NULL)表示,新建一个 Lua 状态机,并使用 skynet_lalloc()内存分配机制,这个定义在malloc_hook.c 中。
6、 luaL_openlibs() 打开 Lua 的标准库。
7、 luaL_loadstring(L, load_config) 用于加载一段Lua 脚本字符串,这个 lua 脚本主要用于打开名字为 config_file 的lua脚本用作 skynet 的配置文件。
8、 lua_pushstring(L, config_file)主要是压入上面加载的 Lua 脚本字符串的参数 config_file,这个参数将由 main()函数的argv[1]参数定义。
9、 lua_pcall(L, 1, 1, 0) 执行加载的 Lua 脚本字符串,这将会加载 config_file 定义的 lua 脚本用于 Skynet 的配置。
10、 _init_env() 弹出 Skynet 配置脚本的 Key 和 Value,并设置为Lua环境变量,最后设置对应Key的值到struct skynet_config config 结构中,方便 skynet_start()调用时传入配置文件的参数。
11、 lua_close(L) 关闭上面用 lua_newstate()创建的 Lua 状态机。
12、 skynet_start()传人配置参数并启动 Skynet 的各个组件和线程。这个函数定义在 skynet_start.c 文件中。
13、 skynet_globalexit()对应上面的 skynet_globalinit(),用于删除线程存储的 Key。
二、skynet_start.c 的流程如下:
1、skynet_start()启动函数,由 main()函数调用。
2、daemon_init()初始化守护进程,由配置文件确定是否启用。这个函数定义在 skynet_daemon.c 中。
3、skynet_harbor_init()初始化节点模块,用于集群,转发远程节点的消息。这个函数定义在 skynet_harbor.c 中。
4、skynet_handle_init()初始化句柄模块,用于给每个 Skynet 服务创建一个全局唯一的句柄值。这个函数定义在 skynet_handle.c 中。
5、skynet_mq_init()初始化消息队列模块,这是 Skynet 的主要数据结构。这个函数定义在 skynet_mq.c 中。
6、skynet_module_init()初始化服务动态库加载模块,主要用于加载符合 Skynet 服务模块接口的动态链接库。这个函数定义在skynet_module.c 中。
7、skynet_timer_init()初始化定时器模块。这个函数定义在skynet_timer.c 中。
8、skynet_socket_init()初始化网络模块。这个函数定义在skynet_socket.c 中。
9、skynet_context_new(logger)加载日志模块。这个函数定义在skynet_server.c 中。
10、bootstrap()加载引导模块。主要在 Skynet 配置文件中定义,默认定义为 bootstrap = "snlua bootstrap",表示引导程序将加载snlua.so 模块,并由 snlua 服务启动 bootstrap.lua 脚本。如果不使用snlua 也可以直接启动其它服务的动态库。
11、_start()函数中创建_monitor()监视线程。线程用 create_thread()创建,create_thread()封装了系统函数 pthread_create()。
12、_start()函数中创建_timer()定时器线程。
13、_start()函数中创建_socket()网络线程。
14、_start()函数中创建_worker()工作线程。工作线程的数量由Skynet 配置文件中的 thread = 8 定义。一般根据服务器的 CPU 核数来设置。
15、skynet_socket_free()释放网络模块。
16、daemon_exit()退出守护进程。这个根据 Skynet 配置文件的需要,决定是否启用。
下图为 skynet_start.c 的整个执行流程:主要功能就是初始化Skynet 的各个组件模块,然后启动监视线程、定时器线程、网络线程和工作线程。
以上就是skynet框架的启动流程,这里采用的是广度优先遍历的方式进行解析,这个方式对于解析框架来说确实是一种很好的学习方式。我们先不管这个接口内部的具体,我们先了解这个接口是做什么。把第一层模块都弄清楚之后,在深入到每个接口去理解。
更多精彩内容,请关注同名公众:一点月光(alittle-moon)