Redis源码漂流记(二)-搭建Redis调试环境
Redis源码漂流记(二)-搭建Redis调试环境
一、目标
搭建Redis调试环境 简要理解Redis命令运转流程
二、前提
1、有一些c知识简单基础(变量命名、常用数据类型、指针等)
可以参考这篇简单入门C语言入门教程 ,
或者B站搜索C语言相关教材(播放量最高的几个均可)。
/*引入头文件,类似java和go中的import包,C#中的using命名空间*/
#include<stdio.h>
int main(void) /*一个简单的C程序*/
{
int number; /*定义个名字叫做number的变量*/
number=2022; /*给number赋一个值*/
printf("This year is %d\n",number); /*调用printf()函数*/
int intsize = sizeof(int);
/*输出:int sizeof is 4 bytes*/
printf("int sizeof is %d bytes\n",intsize);
return 0;
}
/*Redis State of an event based program */
typedef struct aeEventLoop {
/* highest file descriptor currently registered */
int maxfd;
/* max number of file descriptors tracked */
int setsize;
long long timeEventNextId;
aeFileEvent *events; /* Registered events */
aeFiredEvent *fired; /* Fired events */
aeTimeEvent *timeEventHead;
int stop;
/* This is used for polling API specific data */
void *apidata;
aeBeforeSleepProc *beforesleep;
aeBeforeSleepProc *aftersleep;
int flags;
} aeEventLoop;
基本数据类型

2、了解Redis的基本使用
如 set/get等即可。
3、 本地搭建过Ubautu虚拟机或者直接有服务器。
考虑到redis一般安装到linux环境中,所以采取Ubantu进行调试。
“
windows下需要下载redis-windows版本的源码,IDE采用使用Clion搭建redis debug环境或Using GCC with MinGW
”
三、搭建IDE环境
1、安装vscode

2、安装vscode c++扩展
C/C++:提供C/C++支持 Code Runner:提供编译后程序的运行环境 C/C++ Snippets:提供一些常用的C/C++片段,如for(;;){},安装后写代码 方便(tip.如果想要添加自己写的代码段可以点左下角齿轮->用户代码片段) EPITECH C/C++ Headers :为C/C++文件添加头部(包括作者、创建和修改日期等),并为.h头文件添加防重复的宏 Include Autocomplete: 头文件自动补全

3、安装 gcc/gdb
先尝试下面命令安装
sudo apt-get update
sudo apt-get install build-essential gdb
如果不行就尝试下面的。
sudo apt-get install aptitude
sudo aptitude install gcc g++
如果存在依赖性报错安装失败,对建议的方案,第一个no,第二个yes.
检测是否安装成功
gcc -v
g++ -v
gdb -v
make -v
四、检测c文件运行和调试
创建一个目录,用于存放演示文件
mkdir MyCode/src
cd MyCode/src
创建hello.c文件
#include <stdio.h>
int main()
{
puts("HelloC");
return 0;
}
运行:按CodeRunner快捷键【Ctrl+Alt+N】运行代码:
[Running] cd "/home/fcw/MyCode/src/main/" && gcc hello.c -o hello && "/home/fcw/MyCode/src/main/"hello
HelloC
调试: Run-->Start Debugging或 F5调试
选择环境

选择配置


五、下载和编译Redis
1.下载redis源码
// 创建redis目录
mkdir MyCode/redis
cd MyCode/redis
// 下载redis
wget http://download.redis.io/releases/redis-6.2.7.tar.gz
// 解压
tar xzf redis-6.2.7.tar.gz
cd redis-6.2.7/
2、编译Redis
编辑Makefile
(这里也可以打开vsCode编辑复制 )
vim src/Makefile
更新 makefile 下面对应的编译项内容,修改该项的主要目的是为了防止编译优化.
“
O0 -->> O1 -->> O2 -->> O3 (少优化->多优化),
-O0表⽰没有优化,-O1为缺省值,-O3优化级别最⾼”
# --------------------
# OPTIMIZATION?=-O2
OPTIMIZATION?=-O0
# REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS)
REDIS_LD=$(QUIET_LINK)$(CC) $(FINAL_LDFLAGS) $(OPTIMIZATION)
# --------------------
更新前:

更新后

编译redis
make clean; make
Makefile和Make简要说明:
“
需要运行/调试多文件时,Makefile可以设置你想要的编译规则,你想要编译哪些文件,哪些文件不需要编译等等都可以体现在Makefile中,而且支持多线程并发操作,可以减少编译的时间。
”
“
make是用来执行Makefile的,make根据Makefile中写的内容进行编译和链接,make更像是一个批处理的工具,可以批处理源文件,只要执行一条make命令,就可以实现自动编译。当我们编译整个项目工程的时候,make只会编译我们修改过的文件,没有修改过的就不用重新编译,使用make+Makefile极大的提高了我们的工作效率。
”
“
cmake可以生成Makefile文件,支持生成不同平台的Makefile。cmake根据一个叫CMakeLists.txt(手写)的文件生成Makefile。
”
3、配置launch.json
launch.json
随便选中一个c文件。点调试(F5),会提示添加配置。
{
"configurations": [
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/src/redis-server",
"args": [ "${workspaceFolder}/redis.conf"],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
六、调试Redis源码-初探
通过Readme.md可以看到相关文件的简介,例server.c文件
server.c
---
This is the entry point of the Redis server, where the `main()` function
is defined. The following are the most important steps in order to startup
the Redis server.
* `initServerConfig()` sets up the default values of the `server` structure.
* `initServer()` allocates the data structures needed to operate, setup the listening socket, and so forth.
* `aeMain()` starts the event loop which listens for new connections.
There are two special functions called periodically by the event loop:
1. `serverCron()` is called periodically (according to `server.hz` frequency), and performs tasks that must be performed from time to time, like checking for timed out clients.
2. `beforeSleep()` is called every time the event loop fired, Redis served a few requests, and is returning back into the event loop.
Inside server.c you can find code that handles other vital things of the Redis server:
* `call()` is used in order to call a given command in the context of a given client.
* `activeExpireCycle()` handles eviction of keys with a time to live set via the `EXPIRE` command.
* `performEvictions()` is called when a new write command should be performed but Redis is out of memory according to the `maxmemory` directive.
* The global variable `redisCommandTable` defines all the Redis commands, specifying the name of the command, the function implementing the command, the number of arguments required, and other properties of each command.
找到 server.c, main 这是总入口
int main(int argc, char **argv)

aeMain(server.el);//这里面是一个事件循环监听
aeDeleteEventLoop(server.el);
这里接收tcp或socket连接,然后将event及handler放入事件池/bus中。
/* Create an event handler for accepting new connections in TCP or TLS domain sockets.
* This works atomically for all socket fds */
int createSocketAcceptHandler(socketFds *sfd, aeFileProc *accept_handler) {
int j;
for (j = 0; j < sfd->count; j++) {
if (aeCreateFileEvent(server.el, sfd->fd[j], AE_READABLE, accept_handler,NULL) == AE_ERR) {
/* Rollback */
for (j = j-1; j >= 0; j--) aeDeleteFileEvent(server.el, sfd->fd[j], AE_READABLE);
return C_ERR;
}
}
return C_OK;
}
另起一个终端,运行redis-cli,会链接到redis-server,从而调试redis相关源码。
cd MyCode/redis/redis-6.2.7/
./src/redis-cli
找到ae.c,这里是一个while循环监控命令,用于监听新函数的事件循环处理(server.c的main函数会调用这里)。
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
aeProcessEvents(eventLoop, AE_ALL_EVENTS|
AE_CALL_BEFORE_SLEEP|
AE_CALL_AFTER_SLEEP);
}
}
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
找到connection.c
static void connSocketEventHandler(struct aeEventLoop *el, int fd, void *clientData, int mask)
找到connhelper.c
static inline int callHandler(connection *conn, ConnectionCallbackFunc handler) {
connIncrRefs(conn);
if (handler) handler(conn);
connDecrRefs(conn);
if (conn->flags & CONN_FLAG_CLOSE_SCHEDULED) {
if (!connHasRefs(conn)) connClose(conn);
return 0;
}
return 1;
找到networking.c 在 processInputBuffer和processCommandAndResetClient 处打断点
int processCommandAndResetClient(client *c) {
int deadclient = 0;
client *old_client = server.current_client;
server.current_client = c;
if (processCommand(c) == C_OK) {
commandProcessed(c);
}
找到server.c,在 processCommand和call等处打断点
处理命令的总入口。
int processCommand(client *c)

经过了moduleCallCommandFilters、检查是否是quit、lookupCommand、authRequired、ACLCheckAllPerm、cluster_enabled、server.maxmemory、writeCommandsDeniedByDiskError、rejectCommand、blockClient等一系列安全检查逻辑后,来到了执行命令的地方
c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
...
...
/* Exec the command */
if (c->flags & CLIENT_MULTI &&
c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
c->cmd->proc != multiCommand && c->cmd->proc != watchCommand &&
c->cmd->proc != resetCommand)
{
queueMultiCommand(c);
addReply(c,shared.queued);
} else {
call(c,CMD_CALL_FULL);
c->woff = server.master_repl_offset;
if (listLength(server.ready_keys))
handleClientsBlockedOnKeys();
}
void call(client *c, int flags)
...
server.in_nested_call++;
c->cmd->proc(c);
server.in_nested_call--;
找到t_string.c
setCommand
处理命令的堆栈信息如下:

此后将结果写回到客户端
int writeToClient(client *c, int handler_installed) {
/* Update total number of writes on server */
atomicIncr(server.stat_total_writes_processed, 1);
返回结果的堆栈信息如下:

从上面的调试以及堆栈信息可以看出,处理结果和将结果写回到客户端是在两个事件中处理的。
总结redis服务端整个程序流程图如下:

七、环境问题解决
gcc : 依赖: gcc-7(>= 7.3.0-12~) 但是它将不会被安装
sudo apt-get install gcc
出现如下错误:
正在读取软件包列表… 完成
正在分析软件包的依赖关系树
正在读取状态信息… 完成
有一些软件包无法被安装。如果您用的是 unstable 发行版,这也许是
因为系统无法达到您要求的状态造成的。该版本中可能会有一些您需要的软件
包尚未被创建或是它们已被从新到(Incoming)目录移出。
下列信息可能会对解决问题有所帮助:
下列软件包有未满足的依赖关系:
gcc : 依赖: gcc-7(>= 7.3.0-12~) 但是它将不会被安装
E: 无法修正错误,因为您要求某些软件包保持现状,就是它们破坏了软件包间的依赖关系。
使用aptitude包依赖管理工具代替apt来处理,aptitude软件包管理工具在解决依赖性问题上更有优势,具体使用方法如下:
sudo apt-get install aptitude
sudo aptitude install gcc g++
终端中输入后会提示aptitude给出的解决方案,可以选择no,
会继续提供下一个解决方案,但前面的方案会是忽略掉依赖冲突,所以想要彻底解决的话可以跳过前面的几种方案,然后再yes解决。(个人第一次No,第二次Yes)
https://blog.csdn.net/zhutingting0428/article/details/51120949
linux ubuntu gcc编译 fatal error: bits/libc-header-start.h 错误解决
apt-get install gcc-multilib
“
其实主要是gcc安装环境没有安装完善Multilib,顾名思义,就是多重的。
用它完全可以替代原来单一的lib。
这样就既能产生32位的代码,又能生成64位的。比如:64bit机器,同时可以产生32和64两种格式,”
八、引用资料
gcc优化选项-O1-O2-O3-Os优先级 浅析Makefile、make、cmake Debug Redis in VsCode with Gdb 用 gdb 调试 redis Ubuntu下配置VS Code C++ 环境 ubuntu18.04+VScode使用记录(持续更新 在vscode中配置C/C++环境 how-to-install-and-configure-redis-on-ubuntu-20-04 【Redis源码】Redis命令set学习(一)
“
这篇文章整理了一个从服务端和客户端两个维度流程图片
”
九、转载请注明出处
https://www.cnblogs.com/fancunwei ”威行云栈“公众号(微信号:fundeway)

Redis源码漂流记(二)-搭建Redis调试环境的更多相关文章
- 曹工说Redis源码(6)-- redis server 主循环大体流程解析
文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...
- 曹工说Redis源码(2)-- redis server 启动过程解析及简单c语言基础知识补充
文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...
- 曹工说Redis源码(3)-- redis server 启动过程完整解析(中)
文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...
- 曹工说Redis源码(5)-- redis server 启动过程解析,以及EventLoop每次处理事件前的前置工作解析(下)
曹工说Redis源码(5)-- redis server 启动过程解析,eventLoop处理事件前的准备工作(下) 文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis ...
- 曹工说Redis源码(7)-- redis server 的周期执行任务,到底要做些啥
文章导航 Redis源码系列的初衷,是帮助我们更好地理解Redis,更懂Redis,而怎么才能懂,光看是不够的,建议跟着下面的这一篇,把环境搭建起来,后续可以自己阅读源码,或者跟着我这边一起阅读.由于 ...
- Redis源码分析:serverCron - redis源码笔记
[redis源码分析]http://blog.csdn.net/column/details/redis-source.html Redis源代码重要目录 dict.c:也是很重要的两个文件,主要 ...
- 曹工说Redis源码(1)-- redis debug环境搭建,使用clion,达到和调试java一样的效果
概要 最近写了spring系列,这个系列还在进行中,然后有些同学开始叫我大神,然后以为我各方面都比较厉害,当然了,我是有自知之明的,大佬大神什么的,当作一个称呼就好,如果真的以为自己就是大神,那可能就 ...
- eos源码分析和应用(一)调试环境搭建
转载自 http://www.limerence2017.com/2018/09/02/eos1/#more eos基于区块链技术实现的开源引擎,开发人员可以基于该引擎开发DAPP(分布式应用).下面 ...
- redis源码分析(二)-rio(读写抽象层)
Redis io抽象层 Redis中涉及到多种io,如socket与file,为了统一对它们的操作,redis设计了一个抽象层,即rio,使用rio可以实现将数据写入到不同的底层io,但是接口相同.r ...
随机推荐
- 学习Nginx(二)
Nginx支持四层代理 http://nginx.org/en/docs/stream/ngx_stream_core_module.html 该ngx_stream_core_module模块自1. ...
- element-ui 无法对绑定表单的对象中的对象属性进行验证
<el-form-item label="类型" :label-width="formLabelWidth" prop="typeId" ...
- python学习笔记(五)——模块导入
模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py.模块可以被别的程序引入,以使用该模块中的函数等功能.这也是使用 python 标准库的方法. 1.模块的定义与分类 在python中模块实 ...
- python学习笔记(二)——程序结构
1. 选择结构: if 语句:单分支.双分支.多分支 **单分支结构** if 条件表达式: 语句块 **双分支结构** if 条件表达式: 语句块 else: 语句块 **多分支结构** if 条件 ...
- Flex布局在小程序的使用
一篇旧文,上手小程序时做的一些探索 Flex布局是一种十分灵活方便的布局方式,目前主流的现代浏览器基本都实现了对Flex布局的完全支持.而在微信小程序中,IOS端使用的渲染引擎WKWebView和安卓 ...
- 来扯点ionic3[7] LocalStorage的使用—以登录和注销为例
一般意义上,一个互联网 APP 中的数据主自与服务器的交互,但是对于有些数据,我们希望获取到它们以后能保存,并在全局环境使用,比如用户数据--我们不希望在每个页面都从服务器拉取一遍.这时我们就可以利用 ...
- 基于Vue实现关键词实时搜索高亮显示关键词
最近在做移动real-time-search于实时搜索和关键词高亮显示的功能,通过博客的方式总结一下,同时希望能够帮助到别人~~~ 如果不喜欢看文字的朋友我写了一个demo方便已经上传到了github ...
- Canvas 核心技术
最近项目需求中要写较多H5小游戏,游戏本身体量不是很复杂,主要是承载较多业务逻辑,所以决定用canvas来完成游戏部分.之前只是知道H5中有canvas这个东西,也知道它大概是画图的,但具体怎么用,还 ...
- 如何保证同事的代码不会腐烂?一文带你了解 阿里巴巴 COLA 架构
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第1天,点击查看活动详情. 本文开始前,问大家一个问题,你觉得一份业务代码,尤其是互联网业务代码,都有哪些特点? 我能想到的有这几点: ...
- 人机交互BS
B/S结构用户界面设计 [实验编号] 10003809548j Web界面设计 [实验学时] 8学时 [实验环境] l 所需硬件环境为微机: l 所需软件环境为dreamweaver ...
