This article brings you relevant knowledge aboutRedis, which mainly introduces issues related to Makefile files in Redis source code compilation, including Makefile files, src/Makefile files, and all targets. The content of each sub-goal it depends on and other related content, I hope it will be helpful to everyone.

Recommended learning:Redis Tutorial
You need to haveRedis# to learn this article ##Source code, and it is best to set up a relevant compilation environment, so that you can intuitively see the execution process of theMakefilefile. This article "C Encapsulating Redis Operation Function" contains the method of compiling and installingRedis. Readers can read this article first. The source code version used here isredis-6.2.1.
Makefilefile in the source code root directory is as follows:
default: all .DEFAULT: cd src && $(MAKE) $@install: cd src && $(MAKE) $@.PHONY: install
, which has no actual effect and depends on thealltargettarget, so when we usemakedirectly, thedefaulttarget will be called first, and then thealltarget will be called, Since thealltarget does not exist, the.DEFAULTtarget will be called instead. In the execution statement of the Makefile,$@represents the meaning of the target,$(MAKE)representsmake, so the expanded code is as follows. Readers can compile it themselves to see if the first output statement is the same as what we analyzedcd src && make all
directory and then call theMakefilefile in that directory. The only difference is that it is called at this time The target becomesinstall. The expanded code is as follows:cd src && make install
, and then call the corresponding target ofMakefilein the subdirectory, takingcleanas an example, the code is as follows:cd src && make clean
gcccompiler under #Linuxis taken as an example to explain. There is no difference in the rest, just to change some compilation parameters through judgment statements3.1, Makefile.dep target
Before executing the corresponding target, non-target instructions will be executed first, such as variable assignment,Shellstatements, etc., so we will find that,MakefileThe files will not be executed completely in orderThe relevant codes are as follows:
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 || trueifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS))))-include Makefile.dep endif
basis# The usage format of thefindstring
- ##Makefile
is$(findstring FIND, IN), which meansINSearchFIND, if found, returnFIND, if not found, return emptyMakefile- words
#MAKECMDGOALSFunction represents counting the number of words, for example, the return value of$(words, foo bar)is ## of"2"Makefile- Variable represents the parameters passed in (all)
CCMakefile’s- The default value is
is to output a rule forcc## The-MMof#Makefile- make
, which describes the dependency of the source file, but does not include the system header fileThe following information can be summarized:
- 里面的
all目标正是我们前一节说到的那个默认的编译目标,但是我们可以自己试着去编译一下,会发现先生成的是Makefile.dep文件,因为他先执行了最下面那个判断语句,里面调用了Makefile.dep目标- 由于此时
MAKECMDGOALS的值为all,不在NODEPS范围里,所以上面那个ifeq语句成立,会调用Makefile.dep目标REDIS_CC的值由三个变量组成,QUIET_CC是打印调试信息的,读者可以自己去源码看相关内容,这部分不重要,我们忽略,CC的值代表的是编译器,FINAL_CFLAGS里面的值则是编译的一些参数,这些值在上面的代码中都已经摘录出来了- 综上所述
Makefile.dep目标的作用就是生成当前目录下所有以.c结尾的文件的依赖关系,并写入Makefile.dep文件中,编译之后生成的文件内容如下所示,看起来挺乱,但是里面的内容其实将每个源文件最终生成的目标文件给列出来,并且将它需要的依赖列出来而已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.hCopy after login3.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 $<Copy after login以下是对这部分代码的解析:
- 这部分是通用的根据源文件生成目标文件的
target,Makefile中%表示通配符,所以只要符合格式要求的都可以借助这段代码来生成对应的目标文件.make-prerequisites没啥用忽略,而REDIS_CC的值在上一小节有说明了,是用于编译文件的指令gcc的-MMD参数与前面说的那个-MM是基本一致的,只不过这个会将输出内容导入到对应的%.d文件中Makefile中$@表示目标,$<表示第一个依赖,$^表示全部依赖- 综上,这个
target的作用是依赖于一个源文件,然后根据这个源文件生成对应的目标文件,并且将依赖关系导入到对应的%.d文件中下面是一个简单的例子:
# 假设生成的目标文件为acl.o,则代入可得acl.o: acl.c .make-prerequisites $(REDIS_CC) -MMD -o acl.o -c acl.c # 执行完成后在该目录下会生成一个acl.o文件和acl.d文件Copy after login3.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)Copy after login3.4、all目标所依赖的各个子目标的内容
REDIS_LD也是一个编译指令,和前面那个REDIS_CC有点像,只不过这个指定了另外的一些编译参数,比如设置了某些依赖的动态库、静态库的路径,读者有兴趣的话可以去看一下代码,看看REDIS_LD的详细内容FINAL_LIBS是一系列动态库链接参数,读者有兴趣可以自行去Makefile里面查看该变量的内容,限于篇幅原因这里就不展开讲了- 将
QUIET_INSTALL忽略(这个是自定义打印编译信息的),可以看出REDIS_INSTALL的值其实就是install,Linux下的install命令是用于安装或升级软件或备份数据的,这个命令与cp类似,但是install允许你控制目标文件的属性,这里不作深入分析了,有兴趣的读者可以自行查阅相关的介绍install命令的文章。基本用法为:install src des,表示将src文件复制到des文件去
代码如下: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)Copy after login3.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-server3.4.2、REDIS_SENTINEL_NAME目标
可以看到
REDIS_SENTINEL_NAME目标很简单,只是简单地使用install命令复制了REDIS_SERVER_NAME目标生成的那个文件,即redis-server,从这里可以知道哨兵服务redis-sentinel与Redis服务使用的是同一套代码3.4.3、REDIS_CHECK_RDB_NAME目标
和前面的如出一辙,也是简单复制了
redis-server文件到redis-check-rdb文件去3.4.4、REDIS_CHECK_AOF_NAME目标
和前面的如出一辙,也是简单复制了
redis-server文件到redis-check-aof文件去3.4.5、REDIS_CLI_NAME目标
这个就不是简单复制了,而是使用和
REDIS_SERVER_NAME目标相同的方法进行直接编译的,唯一的区别是REDIS_SERVER_NAME链接了…/deps/lua/src/liblua.a,而REDIS_CLI_NAME链接的是…/deps/linenoise/linenoise.o3.4.6、REDIS_BENCHMARK_NAME目标
这个也是使用和
REDIS_SERVER_NAME目标相同的方法进行直接编译的,唯一的区别是REDIS_SERVER_NAME链接了…/deps/lua/src/liblua.a,而REDIS_BENCHMARK_NAME链接的是…/deps/hdr_histogram/hdr_histogram.o3.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 ""Copy after login3.6、安装和卸载Redis的目标
3.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)Copy after login3.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)}Copy after login3.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: distcleanCopy after login3.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)Copy after login4、总结
本文详细地分析了与
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; endifCopy after login所以我们直接去编译的话很多细节会看不到,可以自己尝试修改
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; endifCopy after login本人之前也写过
Nginx编译相关的文章,下面总结两者的几点区别:
Nginxuses a lot ofShellrelated technologies, whileRedisrarely uses theseNginxcross-platform related parameters are configured through the configuration script, whileRedisdoes this directly in theMakefilefile. The two do not What are the advantages and disadvantages?Nginxmainly uses so many configuration scripts for strong scalability, butRedisbasically does not need to consider these, so just implement it simply- Since
Redisputs some of its logic in theMakefilefile, it looks likeNginxis finally generatedMakefileThe file is much simpler and easier to understand thanRedis(NginxThe complex logic is in those configuration scripts)NginxThe generated configuration file has 1000 Multiple lines, the amount of code is much larger than the more than 400 lines ofRedis, becauseNginxlists all the generation methods of all dependencies, andRedisusesMakefile.depand various%.dfiles to disperse dependency information into intermediate files, greatly reducing the amount of code inMakefileRecommended learning:Redis learning tutorial
The above is the detailed content of Detailed explanation of Redis classic techniques: Makefile. For more information, please follow other related articles on the PHP Chinese website!
Commonly used database software
What are the in-memory databases?
Which one has faster reading speed, mongodb or redis?
How to use redis as a cache server
How redis solves data consistency
How do mysql and redis ensure double-write consistency?
What data does redis cache generally store?
What are the 8 data types of redis