Redis篇(2)——main函数

news2024/12/21 2:09:41

众所周知,redis是C语言写的。那么main函数来一波

int main(int argc, char **argv) {
    struct timeval tv;
    int j;

//运行测试方法
#ifdef REDIS_TEST
    if (argc == 3 && !strcasecmp(argv[1], "test")) {
        if (!strcasecmp(argv[2], "ziplist")) {
            return ziplistTest(argc, argv);
        } else if (!strcasecmp(argv[2], "quicklist")) {
            quicklistTest(argc, argv);
        } else if (!strcasecmp(argv[2], "intset")) {
            return intsetTest(argc, argv);
        } else if (!strcasecmp(argv[2], "zipmap")) {
            return zipmapTest(argc, argv);
        } else if (!strcasecmp(argv[2], "sha1test")) {
            return sha1Test(argc, argv);
        } else if (!strcasecmp(argv[2], "util")) {
            return utilTest(argc, argv);
        } else if (!strcasecmp(argv[2], "endianconv")) {
            return endianconvTest(argc, argv);
        } else if (!strcasecmp(argv[2], "crc64")) {
            return crc64Test(argc, argv);
        } else if (!strcasecmp(argv[2], "zmalloc")) {
            return zmalloc_test(argc, argv);
        }

        return -1; /* test not found */
    }
#endif

    /* We need to initialize our libraries, and the server configuration. */
#ifdef INIT_SETPROCTITLE_REPLACEMENT
    spt_init(argc, argv);
#endif
    setlocale(LC_COLLATE,"");
    tzset(); /* Populates 'timezone' global. */
    zmalloc_set_oom_handler(redisOutOfMemoryHandler);
    srand(time(NULL)^getpid());
    gettimeofday(&tv,NULL);

    char hashseed[16];
    getRandomHexChars(hashseed,sizeof(hashseed));
    dictSetHashFunctionSeed((uint8_t*)hashseed);
    server.sentinel_mode = checkForSentinelMode(argc,argv);
    //初始化默认配置
    initServerConfig();
    moduleInitModulesSystem();

    /* Store the executable path and arguments in a safe place in order
     * to be able to restart the server later. */
    server.executable = getAbsolutePath(argv[0]);
    server.exec_argv = zmalloc(sizeof(char*)*(argc+1));
    server.exec_argv[argc] = NULL;
    for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]);

    /* We need to init sentinel right now as parsing the configuration file
     * in sentinel mode will have the effect of populating the sentinel
     * data structures with master nodes to monitor. */
    //哨兵模式
    if (server.sentinel_mode) {
        initSentinelConfig();
        initSentinel();
    }

    /* Check if we need to start in redis-check-rdb/aof mode. We just execute
     * the program main. However the program is part of the Redis executable
     * so that we can easily execute an RDB check on loading errors. */
    //持久化文件检查
    if (strstr(argv[0],"redis-check-rdb") != NULL)
        redis_check_rdb_main(argc,argv,NULL);
    else if (strstr(argv[0],"redis-check-aof") != NULL)
        redis_check_aof_main(argc,argv);

	//加载运行参数/配置文件
    if (argc >= 2) {
        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);
            }
        }

        /* First argument is the config file name? */
        if (argv[j][0] != '-' || argv[j][1] != '-') {
            configfile = argv[j];
            server.configfile = getAbsolutePath(configfile);
            /* Replace the config file in server.exec_argv with
             * its absolute path. */
            zfree(server.exec_argv[j]);
            server.exec_argv[j] = zstrdup(server.configfile);
            j++;
        }

        /* All the other options are parsed and conceptually appended to the
         * configuration file. For instance --port 6380 will generate the
         * string "port 6380\n" to be parsed after the actual file name
         * is parsed, if any. */
        while(j != argc) {
            if (argv[j][0] == '-' && argv[j][1] == '-') {
                /* Option name */
                if (!strcmp(argv[j], "--check-rdb")) {
                    /* Argument has no options, need to skip for parsing. */
                    j++;
                    continue;
                }
                if (sdslen(options)) options = sdscat(options,"\n");
                options = sdscat(options,argv[j]+2);
                options = sdscat(options," ");
            } else {
                /* Option argument */
                options = sdscatrepr(options,argv[j],strlen(argv[j]));
                options = sdscat(options," ");
            }
            j++;
        }
        if (server.sentinel_mode && configfile && *configfile == '-') {
            serverLog(LL_WARNING,
                "Sentinel config from STDIN not allowed.");
            serverLog(LL_WARNING,
                "Sentinel needs config file on disk to save state.  Exiting...");
            exit(1);
        }
        resetServerSaveParams();
        loadServerConfig(configfile,options);
        sdsfree(options);
    }

    serverLog(LL_WARNING, "oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo");
    serverLog(LL_WARNING,
        "Redis version=%s, bits=%d, commit=%s, modified=%d, pid=%d, just started",
            REDIS_VERSION,
            (sizeof(long) == 8) ? 64 : 32,
            redisGitSHA1(),
            strtol(redisGitDirty(),NULL,10) > 0,
            (int)getpid());

    if (argc == 1) {
        serverLog(LL_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
    } else {
        serverLog(LL_WARNING, "Configuration loaded");
    }

    server.supervised = redisIsSupervised(server.supervised_mode);
    int background = server.daemonize && !server.supervised;
    if (background) daemonize();

	//初始化服务器
    initServer();
    if (background || server.pidfile) createPidFile();
    redisSetProcTitle(argv[0]);
    redisAsciiArt();
    checkTcpBacklogSettings();

    if (!server.sentinel_mode) {
        /* Things not needed when running in Sentinel mode. */
        serverLog(LL_WARNING,"Server initialized");
    #ifdef __linux__
        linuxMemoryWarnings();
    #if defined (__arm64__)
        int ret;
        if ((ret = linuxMadvFreeForkBugCheck())) {
            if (ret == 1)
                serverLog(LL_WARNING,"WARNING Your kernel has a bug that could lead to data corruption during background save. "
                                     "Please upgrade to the latest stable kernel.");
            else
                serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
                                      "Your system could be affected, please report this error.");
            if (!checkIgnoreWarning("ARM64-COW-BUG")) {
                serverLog(LL_WARNING,"Redis will now exit to prevent data corruption. "
                                     "Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
                exit(1);
            }
        }
    #endif /* __arm64__ */
    #endif /* __linux__ */
        moduleLoadFromQueue();
        InitServerLast();
        //加载持久化文件内容
        loadDataFromDisk();
        if (server.cluster_enabled) {
            if (verifyClusterConfigWithData() == C_ERR) {
                serverLog(LL_WARNING,
                    "You can't have keys in a DB different than DB 0 when in "
                    "Cluster mode. Exiting.");
                exit(1);
            }
        }
        if (server.ipfd_count > 0)
            serverLog(LL_NOTICE,"Ready to accept connections");
        if (server.sofd > 0)
            serverLog(LL_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
    } else {
        InitServerLast();
        sentinelIsRunning();
    }

    /* Warning the user about suspicious maxmemory setting. */
    if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
        serverLog(LL_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory);
    }

	//设置事件循环的beforeSleep方法
    aeSetBeforeSleepProc(server.el,beforeSleep);
    //设置事件循环的afterSleep方法
    aeSetAfterSleepProc(server.el,afterSleep);
    //事件循环
    aeMain(server.el);
    //退出循环后释放对应的空间
    aeDeleteEventLoop(server.el);
    return 0;
}

上面贴的直接是源码main方法。下面着重分析以下几点
1、如何加载配置的?
在4266行可以看到initServerChonfig方法
在这里插入图片描述
点进去看可以不难发现这里是redis的默认配置
在这里插入图片描述
回到main函数再往下看可以看到,当有配置文件或者启动命令带有的配置参数时会执行loadServerConfig方法覆盖上面initServerConfig的配置
在这里插入图片描述
loadServerConfig具体的逻辑这里就贴上去了,大致就是解析配置文件/命令参数的一个取值,赋值的过程。

2、如何监听客户端请求、启动定时任务的?
继续往下看可以看到4377行的 initServer 方法
在这里插入图片描述
initServer主要就是初始化redisServer结构体,监听配置的端口,创建时间事件循环,文件事件循环,打开aof文件等
伪代码如下:
initServer() {

//…

//监听端口
在这里插入图片描述
//…

//创建时间事件监听:redis本身的一些定时任务,比如更新服务器时间缓存、更新LRU时钟、aof命令的追加等操作
//创建文件事件监听:当服务器有连接请求时执行连接处理器acceptTcpHandler的方法。连接处理器会新建一个套接字anetTcpAccept与客户端createClient,同时并监听套接字对应的文件事件与命令处理器readQueryFromClient做绑定用于处理连接后的命令请求。
在这里插入图片描述
//…

//打开aof文件
在这里插入图片描述
//…

}

3、如何加载持久化文件的?
在4407的loadDataFromDisk
在这里插入图片描述
loadDataFromDisk
在这里插入图片描述
这个方法一看就知道aof和rdb同时开启的时候会以aof优先加载数据。

4、执行事件循环
aeMain即是对一个一个的事件进行处理。上面提到的时间事件,(连接/命令)文件事件。
在这里插入图片描述
在这里插入图片描述
5、写在最后
除此之外,main还有对自带的测试方法、–help,–version等命令,哨兵模式、是否开启了 redis-check-rdb/redis-check-aof 等做了处理。这里就不一一解释了,有兴趣看一眼源码或者是一开始贴的main代码就知道了。

参考文献:《Redis设计与实现》黄健宏著、redis-5.0.14源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot

SpringBoot项目搭建方式1项目搭建方式2SpringBoot文件配置application.propertiesapplication.ymlSpringBoot整合MybatisSpringBoot整合logbackSpringBoot整合pageHelperSpringBoot整合DruidSpringBoot整合FreeMarkerFreeMarker常用指令(遍历List集合)FreeMarker遍历Map集合Spr…

【树莓派不吃灰】Linux系统下必知必会的一些命令

目录1. 前言2. 必知必会命令2.1 ls -al 列出当前目录下的文件2.2 cd 改变目录2.3 pwd 输出当前目录2.4 clear 清屏命令2.5 mkdir 新建目录2.6 rmdir 删除目录2.7 cat 显示或连接文件内容2.8 touch 创建文件 & cat > 创建文件2.9 rm 删除文件2.10 mv 移动/重命名文件/目录…

【小程序开发】事件监听 | 类型划分 | 属性分析

&#x1f373;作者&#xff1a;贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛\color{#FF0000}{贤蛋 大眼萌 &#xff0c;一名很普通但不想普通的程序媛}贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛&#x1f933; &#x1f64a;语录&#xff1a;多一些不为什么的…

基于SSM实现前后端分离在线考试管理系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 文末获取源码 项目编号&#xff1a;BS-XX-…

[kafka]二.优化(如何保证不丢数据,且不重复[一次且仅一次])

二.优化 1.不丢失数据ack-1 当Producer向Leader发送消息时&#xff0c;可以通过ack的值来设置可靠性级别。 1&#xff09;1&#xff08;默认&#xff09; 意味着ISR中的Leader已成功收到消息并且Producer得到Leader收到消息的确认。如果ISR中的副本数还没有来得及拉取数据就…

Linux 进程替换深剖

目录传统艺能&#x1f60e;概念&#x1f914;细则&#x1f914;原理&#x1f914;exec 函数&#x1f914;execl&#x1f60b;execlp&#x1f60b;execle&#x1f60b;execv&#x1f60b;execvp&#x1f60b;execve&#x1f60b;实现简易 shell&#x1f914;传统艺能&#x1f60…

【python】之常用类型(包括进制)之间的转换

个人主页&#xff1a;天寒雨落的博客_CSDN博客-C,CSDN竞赛,python领域博主 特别标注&#xff1a;仅为自己的学习记录笔记&#xff0c;方便复习和加深记忆&#xff0c;仅供借鉴参考&#xff01; 目录 一、字符和整数之间的转换 1.整数转字符 chr(x) 2.字符转整数 ord(x) 二、…

Git操作复习笔记

Git操作复习笔记一、git基础1.1 安装1.2 简单的命令1.2.1 基本工作流程1.2.3 git使用前配置1.2.4 git提交步骤1.2.5 恢复记录1.2.6 撤销二、git分支操作2.1 分支细分2.2 分支命令2.3 暂时保存更改三、github操作3.1 多人协作开发的流程3.2 创建远程仓库3.3 远程仓库克隆到本地仓…

【K8S系列】Kubernetes的网络模型

目录 一、k8s的三种网络 二、service网络 2.1 netfilter 2.2 iptables 2.3 clustip 一、k8s的三种网络 Node Network: 与外部网络接口 Service Network&#xff1a; ipvs规则当中的网络、路由提供调度 Pod Network&#xff1a; 节点当中pod的内部网络无法与外界通信 其中&…

【python初学者日记】selenium初体验——“秒杀商品”、“清空购物车”技能养成记(一)

【python初学者日记】selenium初体验——“秒杀商品”、“清空购物车”技能养成记&#xff08;一&#xff09;用python解决“清空购物车”、“秒杀商品”问题合集&#xff1a;1、Mac版在pycharm和终端上使用pip显示&#xff1a;pip: command not found Mac2、Mac版在pycharm中*…

毛球修剪器方案开发的工作原理和构成

本文介绍了毛球修剪器方案开发的工作原理&#xff1b;不管是羊毛衫、兔子衫还是普通纤维衫&#xff0c;时间一长都不可避免地会有很多毛球。它看起来脏又乱&#xff0c;穿起来特别不雅观。用除毛器剃毛球可以轻松去除毛衣的原始绒毛&#xff0c;而毛衣将失去其原有的保暖性。 原…

HTML登录页面

第一步:构建HTML框架 简介&#xff1a;本文用最通俗的语言&#xff0c;一步步教会大家CSS构建登录页面。 首先构建HTML框架&#xff0c;包含用户名&#xff0c;密码&#xff0c;记住密码&#xff0c;注册这几个功能。 如果大家HTML不牢固&#xff0c;请看我的这篇博客:https:/…

【数据结构】线性表之顺序表详解

&#x1f9d1;‍&#x1f4bb;作者&#xff1a; 情话0.0 &#x1f4dd;专栏&#xff1a;《数据结构》 &#x1f466;个人简介&#xff1a;一名双非研究生的编程菜鸟&#xff0c;在这里分享自己的编程学习笔记&#xff0c;欢迎大家的指正与点赞&#xff0c;谢谢&#xff01; 顺…

32、Java——迷你图书管理器(对象+JDBC)

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;乐趣国学的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

SpringBoot交友APP项目实战(详细介绍+案例源码) - 10.网关配置

系列文章目录 1. 项目介绍及环境配置 2. 短信验证码登录 3. 用户信息 4. MongoDB 5. 推荐好友列表/MongoDB集群/动态发布与查看 6. 圈子动态/圈子互动 7. 即时通讯&#xff08;基于第三方API&#xff09; 8. 附近的人(百度地图APi) 9. 小视频 10.网关配置 文章目录系列文章目录…

【数据结构练习题——查找】

题目&#xff1a;给定如下关键字序列 &#xff08;36,23,51,6&#xff0c;58,48,39,8&#xff0c;88,76,63,17&#xff09; &#xff08;1&#xff09;按表中顺序建立一棵初始为空的二叉排序树&#xff0c;画出该二叉排序树。 &#xff08;2&#xff09;求上述二叉排序树中等…

ikun网站成名录: HTML 中的常用标签用法,从0到1创建一个ikun简介

常见标签(以下均省略了< >) 1.标题标签 h1~h6 2.段落&#xff0c;换行标签 由于html的特性&#xff0c;我们在语句中添加换行&#xff0c;多个空格都是没办法对我们的文本分段落的。如图 所以改用这个标签便可分段了&#xff1a; 用于我们文本可能需要手动换行&#x…

Hadoop高手之路2—Hadoop集群的基础设置

文章目录Hadoop集群的基础设置一、虚拟机软件的安装二、创建虚拟机&#xff0c;安装CentOS1.下载CentOS2.创建虚拟机3.编辑虚拟机设置4.安装centos7.9mini版本5.启动centos&#xff0c;并进行登录6. 退出root登录&#xff0c;用user1登录三、CentOS网络配置1. 查看本地windows主…

图像格式RGB-HSV-YUV

文章目录一、RGB色彩空间二、HSV 色彩空间三、YUV 色彩空间四、色彩空间的转换待更新中FPGA实现RGB与HSV的转换 一、RGB色彩空间 RGB 是最常用于显示器的色彩空间&#xff0c;R(red)是红色通道&#xff0c;G(green)是绿色&#xff0c;B(blue)是蓝色通道。这三种颜色以不同的量…

(附源码)计算机毕业设计SSM教师教学质量评价系统

&#xff08;附源码&#xff09;计算机毕业设计SSM教师教学质量评价系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…