本篇文章带大家聊聊redis源码编译,深入详细解析一下makefile文件,希望对大家有所帮助!
学习本文需要有Redis源码,且最好搭建起相关的编译环境,这样才能直观地看到Makefile文件的执行过程。这里使用的源码版本是redis-6.2.1的。【相关推荐:Redis视频教程】
源代码根目录的Makefile文件内容如下:
default: all .DEFAULT: cd src && $(MAKE) $@ install: cd src && $(MAKE) $@ .PHONY: install
从代码中可以看出以下几点信息:
cd src && make all
cd src && make install
cd src && make clean
该文件是真正起编译作用的文件,内容比较多,比较杂,而且为了兼容多种编译器里面有不少分支选择语法,我们这里只以Linux下的gcc编译器为例去讲解,其余的没区别,就是通过判断语句去改变某些编译参数而已
1、Makefile.dep目标
Makefile在执行对应的目标之前,会先把非目标的指令给执行了,比如变量赋值、Shell语句等等,所以我们会发现,Makefile文件并不会完全按照顺序去执行的
相关代码如下:
NODEPS:=clean distclean # FINAL_CFLAGS里的各个变量原型 STD=-pedantic -DREDIS_STATIC='' WARN=-Wall -W -Wno-missing-field-initializers OPTIMIZATION?=-O2 OPT=$(OPTIMIZATION) DEBUG=-g -ggdb #CFLAGS 根据条件选择的,不重要的参数,忽略 #REDIS_CFLAGS 根据条件选择的,不重要的参数,忽略 FINAL_CFLAGS=$(STD) $(WARN) $(OPT) $(DEBUG) $(CFLAGS) $(REDIS_CFLAGS) REDIS_CC=$(QUIET_CC)$(CC) $(FINAL_CFLAGS) all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) @echo "" @echo "Hint: It's a good idea to run 'make test' ;)" @echo "" Makefile.dep: -$(REDIS_CC) -MM *.c > Makefile.dep 2> /dev/null || true ifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS)))) -include Makefile.dep endif
首先先补充以下几点Makefile的基础
Makefile的findstring函数的使用格式为$(findstring FIND, IN),表示在IN中查找FIND,如果查找到了就返回FIND,找不到就返回空Makefile的words函数表示统计单词数目,例如$(words, foo bar)的返回值为"2"Makefile的MAKECMDGOALS变量表示传入的参数(全部)Makefile的CC默认值是ccMakefile的-MM是输出一个用于make的规则,该规则描述了源文件的依赖关系,但是不包含系统头文件
则可以总结出以下几点信息:
acl.o: acl.c server.h fmacros.h config.h solarisfixes.h rio.h sds.h \ connection.h atomicvar.h ../deps/lua/src/lua.h ../deps/lua/src/luaconf.h \ ae.h monotonic.h dict.h mt19937-64.h adlist.h zmalloc.h anet.h ziplist.h \ intset.h version.h util.h latency.h sparkline.h quicklist.h rax.h \ redismodule.h zipmap.h sha1.h endianconv.h crc64.h stream.h listpack.h \ rdb.h sha256.h adlist.o: adlist.c adlist.h zmalloc.h ae.o: ae.c ae.h monotonic.h fmacros.h anet.h zmalloc.h config.h \ ae_epoll.c ae_epoll.o: ae_epoll.c ... zipmap.o: zipmap.c zmalloc.h endianconv.h config.h zmalloc.o: zmalloc.c config.h zmalloc.h atomicvar.h
2、通用的生成目标文件的target
代码如下:
.make-prerequisites: @touch $@ ifneq ($(strip $(PREV_FINAL_CFLAGS)), $(strip $(FINAL_CFLAGS))) .make-prerequisites: persist-settings endif ifneq ($(strip $(PREV_FINAL_LDFLAGS)), $(strip $(FINAL_LDFLAGS))) .make-prerequisites: persist-settings endif %.o: %.c .make-prerequisites $(REDIS_CC) -MMD -o $@ -c $<
以下是对这部分代码的解析:
下面是一个简单的例子:
# 假设生成的目标文件为acl.o,则代入可得 acl.o: acl.c .make-prerequisites $(REDIS_CC) -MMD -o acl.o -c acl.c # 执行完成后在该目录下会生成一个acl.o文件和acl.d文件
3、all目标所依赖的各个子目标的名称设置
PROG_SUFFIX的值默认为空,可以忽略。这里设置的六个目标名都是会被all这个目标引用的,从名字可以看出这六个目标是对应着Redis不同的功能,依次是服务、哨兵、客户端、基础检测、rdf持久化以及aof持久化。
代码如下:
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX) REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX) REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX) REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX) REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX) REDIS_CHECK_AOF_NAME=redis-check-aof$(PROG_SUFFIX)
4、all目标所依赖的各个子目标的内容
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o release.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ:%.o=%.d) -include $(DEP) INSTALL=install REDIS_INSTALL=$(QUIET_INSTALL)$(INSTALL) # redis-server $(REDIS_SERVER_NAME): $(REDIS_SERVER_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/lua/src/liblua.a $(FINAL_LIBS) # redis-sentinel $(REDIS_SENTINEL_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) # redis-check-rdb $(REDIS_CHECK_RDB_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_RDB_NAME) # redis-check-aof $(REDIS_CHECK_AOF_NAME): $(REDIS_SERVER_NAME) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) # redis-cli $(REDIS_CLI_NAME): $(REDIS_CLI_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/linenoise/linenoise.o $(FINAL_LIBS) # redis-benchmark $(REDIS_BENCHMARK_NAME): $(REDIS_BENCHMARK_OBJ) $(REDIS_LD) -o $@ $^ ../deps/hiredis/libhiredis.a ../deps/hdr_histogram/hdr_histogram.o $(FINAL_LIBS)
4.1、REDIS_SERVER_NAME目标
该目标依赖于REDIS_SERVER_OBJ,而REDIS_SERVER_OBJ的内容都是一些目标文件(上面代码有给出),这些目标文件最终都会通过3.2小节介绍的那个target来生成。可以看到REDIS_SERVER_NAME这个target需要使用REDIS_SERVER_OBJ、…/deps/hiredis/libhiredis.a、…/deps/lua/src/liblua.a以及FINAL_LIBS这些来编译链接生成最终的目标文件,即redis-server
4.2、REDIS_SENTINEL_NAME目标
可以看到REDIS_SENTINEL_NAME目标很简单,只是简单地使用install命令复制了REDIS_SERVER_NAME目标生成的那个文件,即redis-server,从这里可以知道哨兵服务redis-sentinel与Redis服务使用的是同一套代码
4.3、REDIS_CHECK_RDB_NAME目标
和前面的如出一辙,也是简单复制了redis-server文件到redis-check-rdb文件去
4.4、REDIS_CHECK_AOF_NAME目标
和前面的如出一辙,也是简单复制了redis-server文件到redis-check-aof文件去
4.5、REDIS_CLI_NAME目标
这个就不是简单复制了,而是使用和REDIS_SERVER_NAME目标相同的方法进行直接编译的,唯一的区别是REDIS_SERVER_NAME链接了…/deps/lua/src/liblua.a,而REDIS_CLI_NAME链接的是…/deps/linenoise/linenoise.o
4.6、REDIS_BENCHMARK_NAME目标
这个也是使用和REDIS_SERVER_NAME目标相同的方法进行直接编译的,唯一的区别是REDIS_SERVER_NAME链接了…/deps/lua/src/liblua.a,而REDIS_BENCHMARK_NAME链接的是…/deps/hdr_histogram/hdr_histogram.o
5、all目标
经过前面的介绍,all目标的作用也就一目了然了,最终会生成六个可执行文件,以及输出相应的调试信息
代码如下:
all: $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) @echo "" @echo "Hint: It's a good idea to run 'make test' ;)" @echo ""
6、安装和卸载Redis的目标
6.1、安装Redis的目标
这里逻辑很简单,先创建一个用于存放Redis可执行文件的文件夹(默认是/usr/local/bin),然后将REDIS_SERVER_NAME、REDIS_BENCHMARK_NAME、REDIS_CLI_NAME对应的可执行文件复制到/usr/local/bin中去,这里可以看到前面那几个照葫芦画瓢的文件并没有复制过去,而是直接通过创建软连接的方式去生成对应的可执行文件(内容相同,复制过去浪费空间)
代码如下:
PREFIX?=/usr/local INSTALL_BIN=$(PREFIX)/bin install: all @mkdir -p $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_SERVER_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_BENCHMARK_NAME) $(INSTALL_BIN) $(REDIS_INSTALL) $(REDIS_CLI_NAME) $(INSTALL_BIN) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_RDB_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_CHECK_AOF_NAME) @ln -sf $(REDIS_SERVER_NAME) $(INSTALL_BIN)/$(REDIS_SENTINEL_NAME)
6.2、卸载Redis的目标
这里就是删除前面复制的那些文件了,比较简单,就不细讲了
代码如下:
uninstall: rm -f $(INSTALL_BIN)/{$(REDIS_SERVER_NAME),$(REDIS_BENCHMARK_NAME),$(REDIS_CLI_NAME),$(REDIS_CHECK_RDB_NAME),$(REDIS_CHECK_AOF_NAME),$(REDIS_SENTINEL_NAME)}
7、clean和distclean目标
所有Makefile的clean或者distclean目标的作用都是大致相同的,就是删除编译过程中产生的那些中间文件,以及最终编译生成的动态库、静态库、可执行文件等等内容,代码比较简单,就不作过多的分析了
代码如下:
clean: rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep dict-benchmark rm -f $(DEP) .PHONY: clean distclean: clean -(cd ../deps && $(MAKE) distclean) -(rm -f .make-*) .PHONY: distclean
8、test目标
执行完Redis编译之后,会有一段提示文字我们可以运行make test测试功能是否正常,从代码中我们可以看出其实不止一个test目标,还有另一个test-sentinel目标,这个是测试哨兵服务的。这两个目标分别运行了根目录的runtest和runtest-sentinel文件,这两个是脚本文件,里面会继续调用其他脚本来完成整个功能的测试,并输出测试信息到控制台。具体怎么测试的就不分析了,大家有兴趣的可以去看一下。
代码如下:
test: $(REDIS_SERVER_NAME) $(REDIS_CHECK_AOF_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) @(cd ..; ./runtest) test-sentinel: $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) @(cd ..; ./runtest-sentinel)
本文详细地分析了与Redis编译相关的Makefile文件,通过学习Makefile文件里的内容,我们可以更为全面地了解Redis的编译过程,因为Makefile文件中将很多编译命令用@给取消显示了,转而使用它自己特制的编译信息输出给我们看,代码如下:
ifndef V QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; endif
所以我们直接去编译的话很多细节会看不到,可以自己尝试修改Makefile文件,在前面这段代码之前定义V变量,这样就可以看到完整的编译信息了。修改如下:
V = 'good' ifndef V QUIET_CC = @printf ' %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_LINK = @printf ' %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1>&2; endif
本人之前也写过Nginx编译相关的文章,下面总结两者的几点区别:
本文转载自:https://blog.csdn.net/weixin_43798887/article/details/117674538
更多编程相关知识,请访问:编程入门!!
以上就是Redis源码解析之深入了解Makefile文件的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号