這篇文章為大家帶來了關於Redis的相關知識,其中主要介紹了關於Redis原始碼編譯中Makefile檔案的相關問題,包括了Makefile檔案、src/Makefile檔案、all目標所依賴的各個子目標的內容等相關內容,希望對大家有幫助。
推薦學習:Redis教學
#1、前言
學習本文需要有Redis
原始碼,且最好搭建起相關的編譯環境,這樣才能直觀地看到Makefile
檔案的執行過程。這篇文章《C 封裝Redis操作函數》裡有編譯安裝Redis
的方法,讀者可以先看這篇文章。這裡使用的原始碼版本是redis-6.2.1
的。
2、Makefile檔案詳解
原始碼根目錄的Makefile
檔案內容如下:
default: all .DEFAULT: cd src && $(MAKE) $@install: cd src && $(MAKE) $@.PHONY: install
從程式碼中可以看出以下幾點訊息:
- 該檔案的第一個目標是
default
,該目標沒有實際作用,依賴all
目標 - #程式碼中並沒有所謂的
all
目標,所以當我們直接使用make
時,首先會呼叫default
目標,然後呼叫all
目標,由於all
目標不存在,所以會呼叫.DEFAULT
目標來替代,在Makefile的執行語句中,$@
代表的就是目標的意思,$(MAKE)
代表的就是make
,所以展開之後的程式碼如下,讀者可以自行編譯一下,看看第一個輸出語句是否與我們分析的相同
cd src && make all
- install目標和前面的類似,最終也是進去
src/
目錄,然後調用該目錄下的Makefile
文件,區別只在於此時調用的目標變成了install
而已,展開後的程式碼如下:
cd src && make install
- 當傳入參數是其他是,呼叫的都會轉到
. DEFAULT
去,然後去呼叫子目錄下的Makefile
的對應的目標,以clean
為例,程式碼如下:
cd src && make clean
3 、src/Makefile文件詳解
該文件是真正起編譯作用的文件,內容比較多,比較雜,而且為了兼容多種編譯器裡面有不少分支選擇語法,我們這裡只以Linux
下的gcc
編譯器為例去講解,其餘的沒區別,就是透過判斷語句去改變某些編譯參數而已
3.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 || trueifeq (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預設值是
cc
- Makefile
的
-MM是輸出一個用於
make的規則,該規則描述了原始檔案的依賴關係,但是不包含系統頭檔
- 里面的
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.h
3.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 $<p>以下是对这部分代码的解析:</p>
- 这部分是通用的根据源文件生成目标文件的
target
,Makefile
中%
表示通配符,所以只要符合格式要求的都可以借助这段代码来生成对应的目标文件 -
.make-prerequisites
没啥用忽略,而REDIS_CC
的值在上一小节有说明了,是用于编译文件的指令 -
gcc
的-MMD
参数与前面说的那个-MM
是基本一致的,只不过这个会将输出内容导入到对应的%.d
文件中 -
Makefile
中$@
表示目标,$表示第一个依赖,<code>$^
表示全部依赖 - 综上,这个
target
的作用是依赖于一个源文件,然后根据这个源文件生成对应的目标文件,并且将依赖关系导入到对应的%.d
文件中
下面是一个简单的例子:
# 假设生成的目标文件为acl.o,则代入可得acl.o: acl.c .make-prerequisites $(REDIS_CC) -MMD -o acl.o -c acl.c # 执行完成后在该目录下会生成一个acl.o文件和acl.d文件
3.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)
3.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)
3.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
3.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.o
3.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
3.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 ""
3.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)
3.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)}
3.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
3.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)
4、总结
本文详细地分析了与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
编译相关的文章,下面总结两者的几点区别:
-
Nginx
使用了大量的Shell
相關的技術,而Redis
則很少使用這些 -
Nginx
跨平台的相關參數是透過設定腳本進行設定的,而Redis
則是直接在Makefile
檔案中將這件事給做了,這兩者沒有什麼優劣之分,Nginx
主要是為了可擴展性強才使用那麼多配置腳本的,而Redis
基本上不用考慮這些,所以簡單一點實現就行了 - 由於
Redis
將其一些邏輯都放在了Makefile
檔案中了,所以看起來Nginx
最終產生的Makefile
檔案要比Redis
簡單易懂很多(Nginx
複雜邏輯在那些設定腳本裡) -
Nginx
產生的設定檔足有1000多行,程式碼量比Redis
的400多行要大很多,因為Nginx
把全部依賴的生成方式全部列舉了出來,而Redis
借助了Makefile.dep
、各種%.d
檔案來將依賴資訊分散到中間檔案中去,大大減少了Makefile
的程式碼量
推薦學習:Redis學習教學
以上是Redis經典技巧之Makefile檔案詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Redis是现在最热门的key-value数据库,Redis的最大特点是key-value存储所带来的简单和高性能;相较于MongoDB和Redis,晚一年发布的ES可能知名度要低一些,ES的特点是搜索,ES是围绕搜索设计的。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于redis的一些优势和特点,Redis 是一个开源的使用ANSI C语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式存储数据库,下面一起来看一下,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis Cluster集群收缩主从节点的相关问题,包括了Cluster集群收缩概念、将6390主节点从集群中收缩、验证数据迁移过程是否导致数据异常等,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis实现排行榜及相同积分按时间排序,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了关于原子操作中命令原子性的相关问题,包括了处理并发的方案、编程模型、多IO线程以及单命令的相关内容,下面一起看一下,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了bitmap问题,Redis 为我们提供了位图这一数据结构,位图数据结构其实并不是一个全新的玩意,我们可以简单的认为就是个数组,只是里面的内容只能为0或1而已,希望对大家有帮助。

本篇文章给大家带来了关于redis的相关知识,其中主要介绍了Redis实现排行榜及相同积分按时间排序,本文通过实例代码给大家介绍的非常详细,下面一起来看一下,希望对大家有帮助。

redis error就是redis数据库和其组合使用的部件出现错误,这个出现的错误有很多种,例如Redis被配置为保存数据库快照,但它不能持久化到硬盘,用来修改集合数据的命令不能用。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SublimeText3漢化版
中文版,非常好用

DVWA
Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 英文版
推薦:為Win版本,支援程式碼提示!

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。