久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Redis中Makefile文件是什么

153次閱讀
沒有評論

共計 10681 個字符,預計需要花費 27 分鐘才能閱讀完成。

這篇文章給大家分享的是有關 Redis 中 Makefile 文件是什么的內容。丸趣 TV 小編覺得挺實用的,因此分享給大家做個參考,一起跟隨丸趣 TV 小編過來看看吧。

1、前言

這里使用的源碼版本是 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 $

以下是對這部分代碼的解析:

這部分是通用的根據源文件生成目標文件的 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 文件 

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 
QUIET_LINK = @printf   %b %b\n  $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1 
QUIET_INSTALL = @printf   %b %b\n  $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1 
endif

所以我們直接去編譯的話很多細節會看不到,可以自己嘗試修改 Makefile 文件,在前面這段代碼之前定義 V 變量,這樣就可以看到完整的編譯信息了。修改如下:

V =  good 
ifndef V
QUIET_CC = @printf   %b %b\n  $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR) 1 
QUIET_LINK = @printf   %b %b\n  $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1 
QUIET_INSTALL = @printf   %b %b\n  $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR) 1 
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 中 Makefile 文件是什么”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-07-15發表,共計10681字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 阜新| 军事| 孝感市| 郯城县| 都兰县| 龙游县| 芦溪县| 平邑县| 张北县| 土默特右旗| 翁源县| 和林格尔县| 舞阳县| 库尔勒市| 思茅市| 滕州市| 永平县| 太原市| 阳信县| 个旧市| 木兰县| 商丘市| 鲜城| 建昌县| 南部县| 麻江县| 商南县| 车险| 定边县| 密云县| 蓝田县| 和静县| 淳化县| 大新县| 泰安市| 开原市| 南部县| 浦江县| 阿克陶县| 永善县| 正蓝旗|