前言
本文解析skynet的makefile执行流程,格式为 “文件名,代码;讲解” 或者 “文件名,讲解,下一行为代码”
流程1:只输入 make 的情况
1. platform.mk,PLAT ?= none;此时 PLAT 没有设置,所以 none 赋值给 PLAT
2. platform.mk;MAKE 是 make 程序设置的环境变量,值为 make,此时 PLAT = none,构建 default 这个目标要执行的命令是 make none,由于 default 没有依赖对象,所以直接执行 make none
default:
$(MAKE) $(PLAT)
3. platform.mk;随后跳转到 none 这个目标,构建命令就是输出这两行提示,告诉你应该用 make 跟指定平台名,支持 linux freebsd macosx,我们这里就应该使用 make linux
none:
@echo "Please do 'make PLATFORM' where PLATFORM is one of these:"
@echo " $(PLATS)"
4. 因为 none 目标已经执行完成,所以流程结束
流程2:输入 make linux
1. platform.mk,linux : PLAT = linux;直接跳转到构建 linux 的第一个目标处,给 PLAT 赋值为 linux
2. platform.mk,macosx linux : SKYNET_LIBS += -ldl;添加 dlopen 库链接选项
3. platform.mk,linux freebsd : SKYNET_LIBS += -lrt;添加 real_time 库链接选项
4. platform.mk,执行 make all,后面跟上设置的变量值作为后续的编译参数
linux macosx freebsd :
$(MAKE) all PLAT=$@ SKYNET_LIBS="$(SKYNET_LIBS)" SHARED="$(SHARED)" EXPORT="$(EXPORT)" MALLOC_STATICLIB="$(MALLOC_STATICLIB)" SKYNET_DEFINES="$(SKYNET_DEFINES)"
5. Makefile,all : jemalloc;第一个 all 目标依赖 jemalloc,先要构建 jemalloc
6. Makefile,jemalloc : $(MALLOC_STATICLIB);目标 jemalloc 依赖 $(MALLOC_STATICLIB),先要构建 $(MALLOC_STATICLIB)
7. Makefile,MALLOC_STATICLIB := $(JEMALLOC_STATICLIB);目标 MALLOC_STATICLIB 依赖 $(JEMALLOC_STATICLIB),先要构建 $(JEMALLOC_STATICLIB)
8. Makefile,依赖 3rd/jemalloc/Makefile,如果不存在,则需要先构建
$(JEMALLOC_STATICLIB) : 3rd/jemalloc/Makefile
cd 3rd/jemalloc && $(MAKE) CC=$(CC)
9. Makefile,这里的管道符是表示 只有在目标不存在的时候,才会检查该管道符后面的依赖项;这里就是说如果 3rd/jemalloc/Makefile 已经存在了,就不需要在再检查 3rd/jemalloc/autogen.sh 是否存在了;因为是第一次编译,所以还没有生成过 3rd/jemalloc/Makefile,则需要检查 3rd/jemalloc/autogen.sh 是否存在,不存在则跳转到 3rd/jemalloc/autogen.sh 目标构建代码
3rd/jemalloc/Makefile : | 3rd/jemalloc/autogen.sh
cd 3rd/jemalloc && ./autogen.sh --with-jemalloc-prefix=je_ --enable-prof
10. Makefile,下载关联的子库内容,这部分内容不直接打包在 skynet 库中,通过子库的方式存在,方便更新和替换
3rd/jemalloc/autogen.sh :
git submodule update --init
11. 没有更多的依赖关系了,现在开始回溯执行各个目标的命令:
1. git submodule update --init 执行后,3rd/jemalloc/autogen.sh 文件就有了
2. cd 3rd/jemalloc && ./autogen.sh --with-jemalloc-prefix=je_ --enable-prof 执行后,3rd/jemalloc/Makefile 文件就构建好了
3. cd 3rd/jemalloc && $(MAKE) CC=$(CC) 执行后,jemalloc 库就编译好了,第一个 all 目标构建完成,可以看到,skynet 提供了内存管理库的编译扩展,所以用 $(MALLOC_STATICLIB) 来间接指向 $(JEMALLOC_STATICLIB),想切换成其他内存管理库,则新加其他内存管理库的构建流程,然后替换 MALLOC_STATICLIB := $(JEMALLOC_STATICLIB) 这里的库变量名字即可,这就是扩展替代修改的设计原则
12. 开始执行第二个 all 目标的构建
13. Makefile,这里用反斜杠表示不换行,即是后续三排代码其实都是冒号后面的依赖内容,而不是命令
all : \
$(SKYNET_BUILD_PATH)/skynet \
$(foreach v, $(CSERVICE), $(CSERVICE_PATH)/$(v).so) \
$(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so)
14. 解析第二个 all 目标的第一个依赖 skynet,这是最终生成的执行文件
15. Makefile,依赖有 SKYNET_SRC 定义的引擎层 c 代码文件、lua 源码编译生成的静态库 liblua.a、内存管理静态库(默认是 libjemalloc.a)
$(SKYNET_BUILD_PATH)/skynet : $(foreach v, $(SKYNET_SRC), skynet-src/$(v)) $(LUA_LIB) $(MALLOC_STATICLIB)
$(CC) $(CFLAGS) -o $@ $^ -Iskynet-src -I$(JEMALLOC_INC) $(LDFLAGS) $(EXPORT) $(SKYNET_LIBS) $(SKYNET_DEFINES)
- liblua.a 由命令 cd 3rd/lua && $(MAKE) CC='$(CC) -std=gnu99' $(PLAT) 编译而成
- libjemalloc.a 由第一个 all 目标的构建而成,先前已经讲了
16. 解析第二个 all 目标的第二个依赖 $(foreach v, $(CSERVICE), $(CSERVICE_PATH)/$(v).so) ,即是 c 服务模块所在的动态库文件,cservice/*.so
17. Makefile,定义 CSERVICE_TEMP 函数来构建单个 c 服务模块的 .so 库文件
define CSERVICE_TEMP
$$(CSERVICE_PATH)/$(1).so : service-src/service_$(1).c | $$(CSERVICE_PATH)
$$(CC) $$(CFLAGS) $$(SHARED) $$< -o $$@ -Iskynet-src
endef
18. Makefile,遍历 CSERVICE = snlua logger gate harbor 定义的 c 服务名,作为参数传给 CSERVICE_TEMP 函数,通过 eval 函数,展开每个服务库的构建代码到 makefile 文件中,然后在 make 执行时构建完成 cservice/*.so
$(foreach v, $(CSERVICE), $(eval $(call CSERVICE_TEMP,$(v))))
19. 解析第二个 all 目标的第三个依赖 $(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so),即是 skynet 为支持 lua 服务提供的一些 c 代码编写的动态库文件,luaclib/*.so
20. Makefile,依次编译这些 lua 库,他们的使用方式是在 lua 服务中,require "库名.core"
- $(LUA_CLIB_PATH)/skynet.so :
- $(LUA_CLIB_PATH)/bson.so :
- $(LUA_CLIB_PATH)/md5.so
- $(LUA_CLIB_PATH)/client.so :
- $(LUA_CLIB_PATH)/sproto.so :
- $(LUA_CLIB_PATH)/ltls.so :
- $(LUA_CLIB_PATH)/lpeg.so :
21. 到此,skynet 的编译流程全部结束。
结语
总览 skynet 的 makefile 编译目标是:
- 构建框架引擎执行文件 skynet,依赖内存管理库 libjemalloc.a
- 构建 c 服务模块所在的动态库 cservice/xxx.so,例如 cservice/logger.so
- 构建为支持 lua 服务提供的一系列 c 动态库 luaclib/xxx.so,例如 luaclib/skynet.so
skynet 并没有将这两种动态库链接到执行文件 skynet 中,而是通过代码中需要用到的时候动态加载:
- skynet_module.c 中加载 c 服务模块时,通过系统调用 dlopen 来加载 cservice/*.so
- lua 代码中加载 c 动态库时,通过 require xxx.core 来加载 luaclib/*.so,详细流程可见 skynet学习笔记之require xxx.core

本文解析了Skynet的makefile执行流程,包括构建引擎执行文件、C服务模块动态库及Lua服务支持的C动态库等内容。Skynet通过动态加载方式实现对C服务模块和Lua服务的支持。
936

被折叠的 条评论
为什么被折叠?



