skynet源码分析 make
前言
本文的版本选择的是skynet v1.4.0。编译skynet不可以避免的就是make。
本文就具体看看make相关的实现。
关于make可以参考 阮一峰的网络日志。
另外本文只涉及最上层的Makefile,对于子目录下的Makefile就不做分析了。
附录是一些GCC的参数。
正文
当我们在skynet目录下执行make linux 实际上我们就是在执行 Makefile 里的命令。在该文件的首行有该语句:
include platform.mk
platform.mk
逻辑大致如下:
-
判断是否有传入正确的平台linux freebsd macosx 。如果没有,打印错误信息退出: wyw@DESKTOP-GJ20UDC:~/skynet$ make
make none
make[1]: Entering directory '/home/wyw/skynet'
Please do 'make PLATFORM' where PLATFORM is one of these:
linux freebsd macosx
make[1]: Leaving directory '/home/wyw/skynet'
如果传入了错误的平台,会有类似的打印: wyw@DESKTOP-GJ20UDC:~/skynet$ make l
make: *** No rule to make target 'l'. Stop.
-
根据不同的平台设定不同的变量,这里设置的变量会在Makefile中使用到。例如make linux 。执行到最后命令如下: # make all PLAT=linux SKYNET_LIBS="-lpthread -lm -ldl -lrt" SHARED="-fPIC --shared" EXPORT="-Wl,-E" MALLOC_STATICLIB="" SKYNET_DEFINES=""
$(MAKE) all PLAT=$@ SKYNET_LIBS="$(SKYNET_LIBS)" SHARED="$(SHARED)" EXPORT="$(EXPORT)" MALLOC_STATICLIB="$(MALLOC_STATICLIB)" SKYNET_DEFINES="$(SKYNET_DEFINES)"
Makefile
回归到Makefile,在include后设置了一些平台相关的值。并且是执行make all 。
逻辑大致如下:
- 生成jemalloc
- 生成skynet相关的部分
jemalloc
Jemalloc 是一款内存分配器,与其它内存分配器相比,它最大的优势在于多线程情况下的高性能以及内存碎片的减少。
阅读skynet项目中的README.md。其中有句话是这样的:
For Linux, install autoconf first for jemalloc
其原因在稍后我们再揭晓,先来分析Makefile。
起始语句:
# 定义变量MALLOC_STATICLIB
JEMALLOC_STATICLIB := 3rd/jemalloc/lib/libjemalloc_pic.a
MALLOC_STATICLIB := $(JEMALLOC_STATICLIB)
all : jemalloc
# 声明伪目标 jemalloc update3rd
.PHONY : jemalloc update3rd
# 伪目标前提是 $(MALLOC_STATICLIB)
jemalloc : $(MALLOC_STATICLIB)
# 前提是先生成 3rd/jemalloc/Makefile,后执行命令cd 3rd/jemalloc && $(MAKE) CC=$(CC)
# 这一步的make 中需要用到 AUTOCONF := /usr/bin/autoconf
$(JEMALLOC_STATICLIB) : 3rd/jemalloc/Makefile
cd 3rd/jemalloc && $(MAKE) CC=$(CC)
# 生成3rd/jemalloc/Makefile order-only先决条件 从git拉取模块
3rd/jemalloc/Makefile : | 3rd/jemalloc/autogen.sh
# 执行autogen.sh
cd 3rd/jemalloc && ./autogen.sh --with-jemalloc-prefix=je_ --enable-prof
3rd/jemalloc/autogen.sh :
git submodule update --init
skynet
这部分的内容就比较多,先是一些变量的定义。
CSERVICE = snlua logger gate harbor
LUA_CLIB = skynet \
client \
bson md5 sproto lpeg $(TLS_MODULE)
LUA_CLIB_SKYNET = \
lua-skynet.c lua-seri.c \
lua-socket.c \
lua-mongo.c \
lua-netpack.c \
lua-memory.c \
lua-multicast.c \
lua-cluster.c \
lua-crypt.c lsha1.c \
lua-sharedata.c \
lua-stm.c \
lua-debugchannel.c \
lua-datasheet.c \
lua-sharetable.c \
\
SKYNET_SRC = skynet_main.c skynet_handle.c skynet_module.c skynet_mq.c \
skynet_server.c skynet_start.c skynet_timer.c skynet_error.c \
skynet_harbor.c skynet_env.c skynet_monitor.c skynet_socket.c socket_server.c \
malloc_hook.c skynet_daemon.c skynet_log.c
然后是入口 all:
先分析all中的第1句:
all : \
$(SKYNET_BUILD_PATH)/skynet \
LUA_LIB ?= $(LUA_STATICLIB)
# 'cc -g -O2 -Wall -I3rd/lua -o skynet skynet-src/skynet_main.c skynet-src/skynet_handle.c skynet-src/skynet_module.c skynet-src/skynet_mq.c skynet-src/skynet_server.c skynet-src/skynet_start.c skynet-src/skynet_timer.c skynet-src/skynet_error.c skynet-src/skynet_harbor.c skynet-src/skynet_env.c skynet-src/skynet_monitor.c skynet-src/skynet_socket.c skynet-src/socket_server.c skynet-src/malloc_hook.c skynet-src/skynet_daemon.c skynet-src/skynet_log.c 3rd/lua/liblua.a 3rd/jemalloc/lib/libjemalloc_pic.a -Iskynet-src -I3rd/jemalloc/include/jemalloc -Wl,-E -lpthread -lm -ldl -lrt '
# 先决条件中skynet-src下的文件是已经存在的,$(LUA_LIB) 在下文中定义,$(MALLOC_STATICLIB)在前文中已经定义
$(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)
# 执行3rd/lua下的Makefile
$(LUA_STATICLIB) :
cd 3rd/lua && $(MAKE) CC='$(CC) -std=gnu99' $(PLAT)
接着看,all中的第2句:
all: $(foreach v, $(CSERVICE), $(CSERVICE_PATH)/$(v).so) \
$(LUA_CLIB_PATH) :
mkdir $(LUA_CLIB_PATH)
$(CSERVICE_PATH) :
mkdir $(CSERVICE_PATH)
# 定义了模板
define CSERVICE_TEMP
$$(CSERVICE_PATH)/$(1).so : service-src/service_$(1).c | $$(CSERVICE_PATH)
$$(CC) $$(CFLAGS) $$(SHARED) $$< -o $$@ -Iskynet-src
endef
# https://www.gnu.org/software/make/manual/make.html#Eval-Function
# such as: cc -g -O2 -Wall -I3rd/lua -fPIC --shared service-src/service_snlua.c -o cservice/snlua.so -Iskynet-src
$(foreach v, $(CSERVICE), $(eval $(call CSERVICE_TEMP,$(v))))
all中的第3句,对应的是lua的三方库:
all : $(foreach v, $(LUA_CLIB), $(LUA_CLIB_PATH)/$(v).so) $(LUA_CLIB_PATH)/skynet.so : $(addprefix lualib-src/,$(LUA_CLIB_SKYNET)) | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) $^ -o $@ -Iskynet-src -Iservice-src -Ilualib-src$(LUA_CLIB_PATH)/bson.so : lualib-src/lua-bson.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) -Iskynet-src $^ -o $@$(LUA_CLIB_PATH)/md5.so : 3rd/lua-md5/md5.c 3rd/lua-md5/md5lib.c 3rd/lua-md5/compat-5.2.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) -I3rd/lua-md5 $^ -o $@ $(LUA_CLIB_PATH)/client.so : lualib-src/lua-clientsocket.c lualib-src/lua-crypt.c lualib-src/lsha1.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) $^ -o $@ -lpthread$(LUA_CLIB_PATH)/sproto.so : lualib-src/sproto/sproto.c lualib-src/sproto/lsproto.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) -Ilualib-src/sproto $^ -o $@ $(LUA_CLIB_PATH)/ltls.so : lualib-src/ltls.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) -Iskynet-src -L$(TLS_LIB) -I$(TLS_INC) $^ -o $@ -lssl$(LUA_CLIB_PATH)/lpeg.so : 3rd/lpeg/lpcap.c 3rd/lpeg/lpcode.c 3rd/lpeg/lpprint.c 3rd/lpeg/lptree.c 3rd/lpeg/lpvm.c | $(LUA_CLIB_PATH) $(CC) $(CFLAGS) $(SHARED) -I3rd/lpeg $^ -o $@
附录
gcc相关参数:
选项 | 解释 |
---|
-g | 生成调试信息。GNU 调试器可利用该信息。 | -O 或 -O1 | 优化生成代码。 | -O2 | 进一步优化。 | -Wall | 生成所有警告信息。 | -I( i 的大写) | 指定头文件路径(相对路径或觉得路径,建议相对路径) | -Wl,option | 此选项传递 option 给连接程序; 如果 option 中间有逗号, 就将 option 分成多个选项, 然 后传递给会连接程序。 | -E | 只运行 C 预编译器。 | -lpthread | 链接线程库,可以是自己编译的库 | -lm | 编译的时候,链接数学库 | -ldl | 链接显式加载动态库的动态函数库 | -lrt | 链接实时库(real time) | -l (L的小写) | 指定需要链接的库的名字(链接 libc.a :-lc 链接动态库:libc.so : -lc 注意:-l后面直接添加库名省区“lib”和“.so”或“.a” ) | -o | 制定目标名称, 默认的时候, gcc 编译出来的文件是 a.out |
|