管中窥豹之Redis源码分析<一>

来源:互联网 发布:ubuntu交叉编译器 编辑:程序博客网 时间:2024/05/18 02:50

管中窥豹之Redis源码分析<一>

Redis命令行内容解析

在工作中发现Linux下C程序对于进程间通信对于单个节点的共享内存使用越来越频繁,当每个节点都需要反复装载同样的共享内存,对物理资源需求越来越大。随着业务的增长,装载共享内存的数据库表由百万级增加之千万级,随着业务不断发展,需要装载的共享内存大小仍然会继续增加,集群中节点数目的几何增加,对物理资源是一个极大的挑战。

于是乎,独立共享内存,建立一个内存集群的想法油然而生,在目前开源的内存产品种redis集群似乎成了不二的选择。虽然一直关注Redis开源产品很久,系统详细的了解其构成和原理还是很少,萌生出深入了解、剖析Redis源码想法。说到不如做到,就着手阅读Redis源码。


脱离写C代码已经很久,虽然勉强能够看懂C代码,可是很多Redis源代码的细节还是让我觉得是一种艺术,不得不记录下来分享给大家。在此之前也拜读过很多开源爱好者从不同角度剖析Redis源码的文章,切入点不同,看到的Redis产品艺术呈现也不一样。既然阅读源码,本系列的文章主要从程序运行的角度,一步一步剖析源码直至命令的实现和处理方式,逐渐深入分析艺术的呈现。


废话不多说,下面开始管中窥豹,以计算机的方式来阅读Redis源码,如何启动和编译Redis源码不再此处赘述。


众所周知,C程序的入口都是从main函数开始,当然Redis也不例外,源码分析当然得从Redis的server端的源码开始。

Redis的server段程序从redis.c程序开始。

<span style="font-size:14px;">int main(int argc, char **argv)</span>

Redis的server端从上述main函数切入,函数中传入了两个参数argc和argv。其中argc表示执行redis.c程序时带入参数的个数,argv表示传入参数的具体内容。此处,argv采用了一个指向指针的变量argv。这么说可能有点抽象,不妨使用一个具体的例子来解读这么做的好处。

创建一个main函数,执行下述代码:

<span style="font-size:14px;">    char *a = "hello";    char *(*b) = &a;    printf("&b:%p,*b:%s,*b:%p ,b:%p, &a:%p, a:%p, *a:%c, a:%s\n", &b,*b,*b,b, &a, a, *a, a);</span>

这段代码的运行结果:

<span style="font-size:14px;">&b:0xbf867060,*b:hello,*b:0x804873e ,b:0xbf86705c, &a:0xbf86705c, a:0x804873e, *a:h, a:hello</span>

其中b可以类比main函数中的argv变量。

代码片段分析:

    1、定义一个指针类型变量a,并将字符串“hello”的起始地址赋给a

    2、定义一个指向指针类型的变量b,并将a的地址赋给×b,变量b的地址由编译器自动分配

从执行结果来看,b的地址是系统自动分配的,×b的地址为a的地址,×b的内容为a的内容

变量在内存中的表现形式分析如下:

内存分配堆区常量区
*b-->a-->0x804873eh



eb-->0xbf867060

l



l



o

了解了main函数参数传递的过程,回过来分析redis.c中解析服务端解析命令行的参数。

redis.c中下述代码解析了,服务端程序是如何解析命令行参数的。

<span style="font-size:14px;">server.sentinel_mode = checkForSentinelMode(argc,argv);</span>
方法checkForSentinelMode是解析命令行参数的函数。

<span style="font-size:14px;">int checkForSentinelMode(int argc, char **argv) {    int j;    if (strstr(argv[0],"redis-sentinel") != NULL) return 1;    for (j = 1; j < argc; j++)        if (!strcmp(argv[j],"--sentinel")) return 1;    return 0;}</span>
当argv【0】为redis sentinel是表示Redis集群采用主从&&share高可用模式,后面分析Redis集群高可用方式时阐述此过程。

本节源码分析着重分析redis装载参数的配置文件的过程,redis.c程序中判断了集群高可用模式后,服务器端开始装载配置参数,对应源码中的initServerConfig方法。

无论采用哪种高可用模式,server启动的时候都会按照redis.h中宏定义的参数装载redis启动参数,例如:端口为6397,中断时间为10s等,详情见redis.h头文件。


根据执行redis服务端程序带入的参数argv,判断redis输出的结果。

argv【0】表示执行程序的全路径,源码分析采用的是redis-3.0.5,argv【0】则为:redis-server

argv【1】表示带入的第一个字符串参数,例如:redis-server -v;redis-server --version,表示输出当前redis的版本。

由于argv【0】对应为源程序的全路径,所以不带参数的情况下,argc为1;带参数的时候argc的值>=2,所以在redis.c中判断argc>=2的时候开始解析命令行参数。

<span style="font-size:14px;">    if (argc >= 2) {        int j = 1; /* First option to parse in argv[] */        sds options = sdsempty();        char *configfile = NULL;        /* Handle special options --help and --version */        if (strcmp(argv[1], "-v") == 0 ||            strcmp(argv[1], "--version") == 0) version();        if (strcmp(argv[1], "--help") == 0 ||            strcmp(argv[1], "-h") == 0) usage();        if (strcmp(argv[1], "--test-memory") == 0) {            if (argc == 3) {                memtest(atoi(argv[2]),50);                exit(0);            } else {                fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n");                fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n");                exit(1);            }        }</span>
上述代码片段表明,在2个参数的情况下,redis-server程序只有查看帮助和查看版本的两种情况;当3个参数的时候redis-server只有test-memory的情况。其他参数都是默认为config文件方式解析。下个章节进行详细源码分析。

0 0
原创粉丝点击