Redis源碼閱讀【3-Redis編譯與GDB調試】

Redis源碼閱讀【1-簡單動態字符串】
Redis源碼閱讀【2-跳躍表】
Redis源碼閱讀【3-Redis編譯與GDB調試】
Redis源碼閱讀【4-壓縮列表】
Redis源碼閱讀【5-字典】
Redis源碼閱讀【6-整數集合】
Redis源碼閱讀【7-quicklist】
Redis源碼閱讀【8-命令處理生命週期-1】
Redis源碼閱讀【8-命令處理生命週期-2】
Redis源碼閱讀【8-命令處理生命週期-3】
Redis源碼閱讀【8-命令處理生命週期-4】
Redis源碼閱讀【番外篇-Redis的多線程】
建議搭配源碼閱讀源碼地址

1、介紹

本章主要是爲了介紹Redis的編譯方式,以及使用GDB對Redis進行調試的方法,爲後面講解源碼打下基礎,同時也介紹GDB這個C語言代碼調試利器的部分使用

2、Redis編譯

Redis編譯應該是一個必須掌握的技能,畢竟很多時候我們並不一定會直接使用Redis編譯好的包類型,可能會更具當前操作系統的情況,調整部分源碼編譯後再使用,例如Redis5當前的版本是不支持Windows的,如果大家想要使用Windows版本的Redis最好去官網下載Redis3以及之前的版本去自行編譯使用,此外當大家希望能在Redis中加入一些日誌或者其它東西的時候,一樣需要修改源碼進行編譯,那麼下面就介紹一下官方的推薦的編譯步驟(很簡單的)

2.1、獲取源碼

自行編譯的第一步當然就算獲取源碼,Redis獲取源碼的方式最直接的就是github,這裏我貼出Redis的github地址:https://github.com/antirez/redis,或者你可以通過github搜索Redis:在這裏插入圖片描述
當然github上面的是最新版本的代碼,並非穩定版本代碼,許多新版本的特性會先在github中放出,讓大家下載調試體驗,當然線上穩定版本還是建議去官網下載Stable版本,地址:https://redis.io/download
在這裏插入圖片描述

2.2、目錄介紹

其中這個antirez/redis就是Redis在github上面官方的地址了。下載好後源碼,我們可以隨便導入到一個IDE裏面查看,這裏我使用的是Clion
在這裏插入圖片描述
項目結構如上圖所示:

目錄/文件 說明
deps Hiredis 模塊(輕量級訪問redis的客戶端),linenoise模塊(命令行編輯輔助,能實現自動補全) ,字典的定義實現,sds的定義實現,redis封裝的malloc,Lua等
src 核心源碼,大多功能都在這塊實現,其中Redis自行實現了網絡框架在這個目錄中(ae_開頭的文件),Redis並未使用libevent作爲網絡模塊
tests 測試模塊,用來測試Redis的代碼,大多測試用例和腳本都在這裏面
utils Redis的工具實現
redis.conf Redis官方源碼默認提供的配置文件
Makefile Redis C語言編譯使用的Makefile,當然你也可以使用Cmake進行管理,當然Cmake支持的不是很好

2.3、編譯前的準備

在編譯前,我們需要進行一些準備工作,我們本人是在Ubuntu-18上面進行編譯的,Linux環境是推薦環境,Redis5目前只適配了Linux,並沒有適配Windows,那麼在Linux上進行編譯本身我們也需要準備和更新一些內庫,這裏我當且認爲你已經按照好了Ubuntu

1、更新Ubuntu到最新版本

sudo apt update

更新系統自帶內庫等等

2、下載編譯必備內庫,內包含一些C/C++必備的庫

sudo apt install build-essential
sudo apt install gcc-c++

3、除此外可能會使用到的內庫

apt-get install libqt4-* libqml*

4、如果出現has no member named(這個問題是由於官方升級了gcc導致的)

升級到 5.3及以上版本
yum -y install centos-release-scl
yum -y install devtoolset-9-gcc devtoolset-9-gcc-c++ devtoolset-9-binutils
//這裏退出xhell會恢復gcc版本
scl enable devtoolset-9 bash
//永久替換
echo "source /opt/rh/devtoolset-9/enable" >>/etc/profile

5、安裝測試工具tcl

sudo apt install tcl

2.4、開始編譯

一切準備好後,準備開始編譯,進入到源碼目錄底下,項目的根目錄

直接編譯

make #直接編譯

make MALLOC=libc # 指定使用 libc對 malloc 編譯

make MALLOC=jemalloc # 指定使用 jemalloc 編譯但是要先安裝jemalloc依賴

當然你也可以編譯成32位版本

 make 32bit

執行後結果如下:
在這裏插入圖片描述
這裏會提示你需要使用 make test 來執行內部的自測用例(test文件夾裏面),來判斷本次編譯是否完全成功

執行 make test

sudo apt install tcl # 需要先安裝tcl
make test

過程如下:
在這裏插入圖片描述
當出現這句化的時候證明自測已經成功
在這裏插入圖片描述
All tests passed withour errors!

這個時候進入項目根目錄底下的src目錄中你會發現幾個已經編譯好的二進制文件,是可以直接運行的
在這裏插入圖片描述
這兩個就是服務端和客戶端的運行入口了,其中默認的配置文件在外層根目錄(redis.conf)

運行 ./redis-server 或者 ./redis-server …/redis.conf

./redis-server

結果如下
在這裏插入圖片描述
這個時候Redis服務端已經運行起來了。我們再打開一個終端,運行下面的指令:

root@zengshilin-virtual-machine:/home/test/redis-5.0.7/src# ./redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> get test
"hello"

到這裏我們的Redis已經完全編譯成功了

3、GDB調試

GDB是unix/linux的常用調試工具,其中GDB支持man手冊查閱,在Liunx這種經常沒有可視化界面的操作系統底下,GDB是一種經常使用的調試工具,那麼這裏我也推薦大家使用GDB來調試已經編譯好的Redis代碼,其中Redis官網對GDB的介紹如下:http://redis.io/topics/debugging在這裏插入圖片描述
可見官方也是推薦GDB來調試Redis

3.1、安裝GDB

在Ubuntu底下安裝GDB使用這條指令即可

sudo apt-get install gdb

3.2、GDB調試編譯好的Redis

3.2.1、GDB模式啓動

進入到剛剛編譯好的Redis目錄src底下,輸入指令

gdb redis-server

結果如下:
在這裏插入圖片描述
此時剛剛編譯好的redis-server已經進入到gdb的調試模式下,但是當前還未運行,然後我們啓動以下redis服務端,使用 r 或者 r …/redis.conf

(gdb) r ../redis.conf

在這裏插入圖片描述
可以看到當前的Redis在GDB調試模式下啓動了,這個使用我們再開一個新的窗口運行redis-cli查看效果如下:

127.0.0.1:6379> set test hello
OK
127.0.0.1:6379> get test
"hello"
127.0.0.1:6379> keys *
1) "test"
127.0.0.1:6379> 

可見服務端當前也是可用狀態的

3.2.2、在Redis源碼上打斷點

GDB個其它調試一樣,可以打斷點在目標代碼上面,同時也支持單步調試等操作,我們在服務斷t_string.c文件上面的set命令打上斷點看看是否生效,代碼位置如圖所示:
在這裏插入圖片描述
我們將斷點打在setCommand上面,在gdb模式下執行:

(gdb) b setCommand
Breakpoint 1 at 0x609c0: file t_string.c, line 96.

斷點已經打好,這時候我們在gdb模式下啓動Redis

(gdb) r ../redis.conf

然後我們打開一個新終端,運行 redis-cli 輸入以下命令

127.0.0.1:6379> set test hello

可以看到服務斷那邊已經執行到斷點的位置
在這裏插入圖片描述
這時我們查看以下redis的運行棧
在這裏插入圖片描述
瞭解計算機操作系統的同學應該都知道,程序調用是基於棧的,所以這裏打印出當前線程運行所經歷的調用棧

我們通過以下指令來嘗試GDB調試:

(gdb) n # 單步執行
102	    for (j = 3; j < c->argc; j++) {
(gdb) n
100	    int flags = OBJ_SET_NO_FLAGS;
(gdb) n
99	    int unit = UNIT_SECONDS;
(gdb) n
98	    robj *expire = NULL;
(gdb) n
138	    c->argv[2] = tryObjectEncoding(c->argv[2]);
(gdb) s # 進入函數內部
tryObjectEncoding (o=0x7ffff6c0e0c0) at object.c:431
431	robj *tryObjectEncoding(robj *o) {
(gdb) p *o # p 是 print的意思 打印當前變量
$1 = {type = 0, encoding = 8, lru = 454090, refcount = 1, ptr = 0x7ffff6c0e0d3}
(gdb) p o->type # 打印變量的成員屬性
$2 = 0
(gdb) 

以上就算GDB調試的大致用法,也介紹大家可以多去官網看看gdb的其它使用技巧:http://www.gnu.org/software/gdb/

4、結束

這篇文章我們介紹了Redis在linux低下使用源碼編譯的過程,以及如何使用GDB調試工具來斷點調試已經編譯好的Redis,後續文章我也會不時使用GDB的方式來調試Redis的代碼運行過程,以便更好的理解Redis的源碼執行過程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章