- VisualStudio2022插件的安装及使用-编程手把手系列文章
- pprof-在现网场景怎么用
- C#实现的下拉多选框,下拉多选树,多级节点
- 【学习笔记】基础数据结构:猫树
Redis 实际上是简称,全称为 Remote Dictionary Server (远程字典服务器),由 Salvatore Sanfilippo 写的高性能 key-value 存储系统,其完全开源免费,遵守 BSD 协议。Redis 与其他 key-value 缓存产品(如 memcache)有以下几个特点.
Redis 也是一种 分布式缓存 ,其代码是 c 语言写的,那我们该如何阅读呢?
环境依赖,先看看 gcc 、cc、g++ 有没有安装 。
whereis gcc
whereis cc
whereis g++
安装gcc 。
xcode-select --install
brew install gcc
brew install pkg-config
查看 gcc 的版本:
$ gcc --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: x86_64-apple-darwin22.1.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
我使用 CLion 2022.3.1 ,这个版本可以支持 Makefile 的项目,我们可以检查一下环境是不是有问题, 如果有问题,这里会有错误信息,我的之前报错是因为 Clion 的版本版本太低了,升级之后就好了.
下载Redis源码:
git clone https://github.com/redis/redis.git
切换到指定的版本 。
git checkout 7.0
File => New CMake Project from Sources, 打开源码项目, 会自动生成根目录下的 CMakeList.txt 文件: Clion 导入项目的时候选择已有的 MakeFile 文件,如果有是否 clean 项目,选择 clean 即可,之后可以点开 MakeFile 文件:
如果需要禁止编译器优化,可以使用下面命令:
make CFLAGS="-g -O0" MALLOC=jemalloc
运行完之后, Src 文件下就会出现可运行文件:
然后可以看到这些可运行的选项,继而配置Edit configuration 运行配置:
选择 debug 进行启动,启动成功,然后可以进行调试了:
可以使用 Redis Desktop Manager 来进行连接:
或者命令行连接(没有密码就可以不需要 -a 12345):
redis-cli -h 127.0.0.1 -p 6379 -a 12345
如果头文件引入报红色下划线,那就试试重新加载一下 。
Redis 的目录:
网上的源码阅读顺序(引自网上):
从大方向来说,学习 Redis 会有两种路径:
个人建议是先学习如何启动 Redis,抓大放小(大致知道哪个类启动,读那些配置文件,大概是做什么用的),学习 Redis 到底能干什么,大致知道 Redis 的一些用法之后,再去了解 Redis 的常用的数据结构,到底怎么实现的,这个时候对 Redis 的一些数据结构大致有印象,之后可以跟着 Redis 启动,执行命令去看具体功能执行的路径。 在 Debug 的过程中,可以加深影响,更加了解数据结构的设计,代码的调用关系.
在C语言中,常量是使用频率很高的一个量。常量是指在程序运行过程中,其值不能被改变的量。常量常使用 #define来定义。 使用#define定义的常量也称为符号常量,可以提高程序的运行效率,Redis 的源代码中有比较多的地方都使用该方式.
一般有以下两种用法:
#define 宏名 宏值
#define 宏名(参数列表) 表达式
第一种就是定义常量,比如:
#define N 100
此后直到 #undef N之前, N的值都是100。当遇到#undef N,其后如果再出现 N,则 N 需要重新定义之后才可以使用.
第二种语法常用来定义符号函数。 例如:
#define AREA(x,y) (x)*(y)
表示用来求长和宽分别是x和y的矩形的面积。 需要注意的是,在表达式(x) * (y)中,x和y都要使用“()”括起来,这是因为符号函数在编译时时进行符号形式替换。如果不加()则可能会发生意想不到的错误,例如:
#define AREA(x,y) x*y
...
A = AREA( 2+3, 1+2 );
此处预期的结果是15,但是实际的结果却是7,这是因为该段代码在编译进行了简单的符号替换而得到的实际表达式是: A = 2+3 * 1+2,
根据运算符的优先级,先进行乘法运算,然后才是加法,这就导致了错误。 而如果使用 。
#define AREA(x,y) (x)*(y)
...
A = AREA( 2+3, 1+2 );
则在编译时替换的结果是:
A = (2+3) * (1+2);
#include"stdio.h"
#define AREA(x,y) (x)*(y)
int main()
{
int a = AREA(2+3, 1+2);
printf( " %d\n", a);
return 0;
}
Redis 是使用 c 语言写的,里面有很多头文件
#include "server.h"
#include "monotonic.h"
#include "cluster.h"
#include "slowlog.h"
#include "bio.h"
#include "latency.h"
#include "atomicvar.h"
#include "mt19937-64.h"
#include "functions.h"
#include "syscheck.h"
#include <time.h>
以 < 开头的,比如 #include <time.h> 是标准库的头文件,会在系统指定路径下查找,对应到 Java里面可以理解为 官方的 jdk 里面的类,而类似 #include "server.h" 则是工程里面自定义的.
我没怎么写过 c 语言的代码, 一般 .c 文件是写实现的代码逻辑的,那如何在 a 文件里面写一个方法,让 b 文件也能用呢?
通过头文件的机制,类似 Java 里面的 接口, public 和 private 的概念,Java 中 一般希望对外暴露的方法,会设置为 public ,,如果不希望暴露,则设置为private。c 语言里面如果希望暴露,则可以在头文件里面定义,否则不用定义。(虽然c语言是面向过程的,但是Redis确实在里面实践一些面向对象的思想).
比如计算两数之和 与 两数之差 的乘积 test.c 。
long long mul(int a,int b) {
return a*b;
}
long long calculate(int a,int b) {
return mul(a+b,a-b);
}
暴露出去的头文件test.h 。
long long calculate(int a,int b);
运行的代码 main.c ,可以正常计算结果为 -3
#include "stdio.h"
#include "test.h"
int main(){
printf("结果:%lld",calculate(1,2));
return 0;
}
但是如果直接引用 sum() 方法,则会报错,无法使用:
如果我们多次引用头文件会怎么样?结果是正常运行:
Redis 里面有挺多的地方定义头文件的时候总是来一句 #isdef 或者 ifndef 。
#ifdef __linux__
#include <sys/mman.h>
#endif
#ifndef __ADLIST_H__
#define __ADLIST_H__
...
#endif /* __ADLIST_H__ */
如果加了 #ifndef ,则会判断只有没有定义这个宏的时候,才会定义它,第二次再次遇到 include 的时候,发现这个宏已经被定义过了,就会直接跳过,这样可以保证多次 include 也不会被解析多次,有且只有一次.
解析多次的坏处是什么?
.h
文件里面定义了全局变量,会导致变量重复定义。这个基本不太会,公司编码规范一般都会禁止,这样写是不人道的。既然禁止了在 .h 文件里面定义全局变量,那全局变量在哪里定义呢?当然是 .c 文件,比如 Redis 里面的全局变量:
那其他的文件怎么使用?这个 sever 可是全局唯一的,维护了 redis 的全部状态数据,那当然是暴露出去,在哪里暴露出去,在 .h 文件,使用关键字 extern 。
阅读源码,是一件长期的事情,但是我们每次跟读代码的时候,一定要带着问题去阅读,否则效率会下降挺多。前期了解数据结构模型的时候,可以在网上找一些简单易懂的博客,最好是有图片的,书籍比较推荐《Redis 设计与实现》。有一定了解之后,会有些疑问,不用担心,此时再通过读源代码去验证我们的想法,可能不少小伙伴没学过 c 语言,也不必担心,语言之间都是相通的,其次即使有关键字不会,可以通过搜索也可以快速了解其作用。 希望我们都能从全局看功能 --> 实践 --> 抓大放小 --> 带疑问看源码 --> 重构知识图谱 --> 关联知识 --> 跳出细节俯瞰全局,最终完成 Redis 相关的知识学习,并形成一套自己的方法论.
作者:秦怀 。
最后此篇关于Redis【1】-如何阅读Redis源码的文章就讲到这里了,如果你想了解更多关于Redis【1】-如何阅读Redis源码的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试读取一个大型日志文件,该文件已使用不同的分隔符(遗留更改)进行了解析。 此代码有效 import os, subprocess, time, re import pandas as pd f
我试图理解在 Linux 下以 Turbo 模式(特别是 fpc -Mtp -vw)编译的 Free Pascal 中看到的有点神奇的行为。代码来自 Jack Crenshaw 的“让我们构建一个编译
我有一个具有以下结构的 txt 文件: NAME DATA1 DATA2 a 10 1,2,3 b 6 8,9 c 2
我试图理解在 Linux 下以 Turbo 模式(特别是 fpc -Mtp -vw)编译的 Free Pascal 中看到的有点神奇的行为。代码来自 Jack Crenshaw 的“让我们构建一个编译
public class Bug1 { private String s; public void Bug1(){ s = "hello"; } public Stri
我们有这样一种情况,我们的应用程序需要处理一系列文件,而不是同步执行此功能,我们希望采用多线程将工作负载分配给不同的线程。 每一项工作是: 1.以只读方式打开文件 2.处理文件中的数据 3.将处理后的
我正在尝试读取 .php 文件并替换十六进制字符。php文件格式如下: 问题是它弄乱了转义字符 (\") 到目前为止我的代码: while(i=48 && str[i+2]=97 && str[i+
我正在用 C# 开发一个程序,我需要一些帮助。我正在尝试创建一个数组或项目列表,显示在某个网站上。我想要做的是阅读 anchor 文本,它是 href。例如,这是 HTML:
我有一个偏好设置,它控制我的应用程序是否在用户单击按钮时播放声音(这种情况经常发生,想想计算器)。每次用户单击按钮时,都会调用以下方法: private void playButtonClickSou
我正在尝试在我的标签末尾创建一个阅读更多按钮。我希望它默认显示 3 行。我正在用 swift 而不是 objective c 编写代码。只有当用户点击标签的阅读更多部分时,标签才会展开。它的外观和工作
当您获得第三方库(c、c++)、开源(LGPL 说)但没有很好的文档时,了解它以便能够集成到您的应用程序中的最佳方法是什么? 该库通常有一些示例程序,我最终使用 gdb 浏览了代码。还有其他建议/最佳
同时从 2 个或更多不同线程对同一个文件描述符使用 pread 是否有问题? 最佳答案 pread 本身是线程安全的,因为它不在 list of unsafe functions 上.所以调用它是安全
当您使用命令 pd.read_csv 读取 csv 时,如何跳过连续包含特定值的行?如果在第 50、55 行,第一列的值为 100,那么我想在读取 csv 文件时跳过这些行。我如何将这些命令放入像 p
我迫切需要在 C# 中使用 T4 生成 HTML 输出。 我正在使用 Runtime-T4-Files 并选择“TextTemplatingFilePreprocessor”而不是“TextTempl
今年夏天我在实习期间一直在学习 ERP 应用程序。由于我是一名即将毕业的程序员,我希望有一个可靠的软件分支可以帮助我完成工作,直到我确定下一步该做什么(直到我对大局有一个很好的了解)。到现在为止,我刚
将包含列(例如“a”、“b”)的数据帧保存为 parquet,然后在稍后的时间点读取 parquet 不会提供相同的列顺序(可能是“b”、“a”fe)文件保存为。 不幸的是,我无法弄清楚订单是如何受到
我正在开发一个使用谷歌表格作为数据库的应用程序,但我不知道如何让 Swift 从谷歌表格中读取。我浏览了 API 网站和一些问题,但刚开始我需要一些帮助。到目前为止,我有; 私有(private)让范
我打算阅读swing concept,如果值得一读,请推荐一些学习 Material 最佳答案 自 AWT 崩溃以来,Java 的 GUI 工具包太多了。即使是 Swing 也被评论家严重低估,但他们
我已经使用 J 几个月了,我发现阅读不熟悉的代码(例如,不是我自己写的)是该语言最具挑战性的方面之一,尤其是在默认情况下。过了一会儿,我想出了这个策略: 1)将代码段复制到word文档中 2)从(1)
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我是一名优秀的程序员,十分优秀!