Redis-server中的MAIN函数

我们从redis的main函数着手,来分析整个redis的启动过程,main函数在redis.c/redis.h中的第2977行

#ifdef INIT_SETPROCTITLE_REPLACEMENT
        spt_init(argc, argv);
#endif
此处用来修改进程名

setlocale(LC_COLLATE,"");
设置Redis所使用的字符串编码。

zmalloc_enable_thread_safeness();
设置线程安全zmalloc开关,函数存在于zmalloc.c中的第237行,作用是将zmalloc_thread_safe这个变量设置为1,该变量大多数情况下是用作是否采用加锁操作的布尔判断。

zmalloc_set_oom_handler(redisOutOfMemoryHandler);
zmalloc_set_oom_handler的函数在zmalloc.c中的241行,用来设置内存溢出之后的处理函数,而redisOutOfMemoryHandler在redis.c的第2960行,在Redis内存溢出之后记录报警日志,然后退出整个程序。

dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
函数存在于dict.c中的第90行,使用gettimeofday(&tv,NULL);所取到的精确时间和当前进程的pid异或来生成DICT中的哈希函数的种子。

server.sentinel_mode = checkForSentinelMode(argc,argv);
    ...
    if (server.sentinel_mode) {
        initSentinelConfig();
        initSentinel();
    }
根据输入的参数判断是否以sentinel模式运行,Redis-sentinel是Redis的集群管理工具,主要是中心节点用来监控其他节点的工作情况并进行故障恢复。

initServerConfig();
该函数在redis.c中的第1275行,以默认值初始化server的各项属性,具体属性可参见redis.h中第569行所定义的结构体的各项属性及其注释。

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);
            }
        }

/* First argument is the config file name? */
        if (argv[j][0] != ‘-‘ || argv[j][1] != ‘-‘)
            configfile = argv[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 (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 (configfile) server.configfile = getAbsolutePath(configfile);
        resetServerSaveParams();
        loadServerConfig(configfile,options);
        sdsfree(options);
    } else {
        redisLog(REDIS_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");
    }
这是redis.c中的第3001-3048行,主要是用来处理Redis-server命令行的一些可选参数选项以及读取参数配置文件内容并初始化server的全局变量的相关代码。

if (server.daemonize) daemonize();
根据server.daemonize的值决定是否以daemon的方式开启Redis,函数位于redis.c中的第2837行。

initServer();
初始化整个服务器,函数位于redis.c中的第1519行。主要是初始化各项属性的队列、建立对端口和UNIX域套接字的监听、生成AOF文件句柄等等。

if (server.daemonize) createPidFile();
    redisSetProcTitle(argv[0]);
    redisAsciiArt();
这三行分别是创建pid文件,设置程序名称以及打印启动时大家都会看到的LOGO。

if (!server.sentinel_mode) {
        /* Things not needed when running in Sentinel mode. */
        redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
    #ifdef __linux__
        linuxOvercommitMemoryWarning();
    #endif
        loadDataFromDisk();
        if (server.ipfd_count > 0)
            redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
        if (server.sofd > 0)
            redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
    } else {
        sentinelIsRunning();
    }
这里主要是处理sentinel模式和普通模式下,redis中的数据恢复问题。这里我们暂时只看非sentinel模式下的处理方式。其中,除了一些LOG函数之外,有两个特别的函数linuxOvercommitMemoryWarning();和loadDataFromDisk();。

linuxOvercommitMemoryWarning();位于redis.c的第2821行,主要是用来检查在Linux下运行时,检测关于Linux内核对于内存分配方式策略的选择。该参数位于/proc/sys/vm/overcommit_memory,其值可以是0、1、2。这三个值分别代表的意义是:

0表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。也就是说该模式下将允许轻微的overcommit,而后果严重的将失败。
1表示内核允许分配所有的物理内存,而不管当前的内存状态如何。也就是说无论怎样都会给程序分配新的内存空间。
2表示内核将根据某个值来决定是否分配内存,这个值为swap+某个比例(默认值为50%)下的RAM。超过该值得内存分配申请都将失败。
若Linux中该参数的值为0,则REDIS将LOG一条警告信息。

loadDataFromDisk();函数位于redis.c中的第2944行,根据之前初始化配置信息的内容决定是采用AOF或是RDB方式读取磁盘数据。

/* Warning the user about suspicious maxmemory setting. */
    if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
        redisLog(REDIS_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);
    }
如源码注释所说,告诉使用者是不是maxmemory这个配置项的数值写错了,因为只指定了不到1MB的空间。

aeSetBeforeSleepProc(server.el,beforeSleep);
    aeMain(server.el);
aeSetBeforeSleepProc(server.el,beforeSleep);用来设置每次进入事件处理函数之前所需要执行的函数。而aeMain(server.el);函数,位于ae.c中的第450行,在经过了之前的一系列的操作之后,真正的开始Redis事件处理循环。

aeDeleteEventLoop(server.el);
    return 0;
如果事件轮询结束,释放之前申请的资源,并且退出函数。

更多精彩内容请关注:http://bbs.superwu.cn

关注超人学院微信:BJ-CRXY

时间: 2024-08-03 16:47:08

Redis-server中的MAIN函数的相关文章

惊天发现之&quot;c#中的Main函数不能调同一个类中的非静态方法&quot;

这是什么原因呢?求大神指点!惊天发现之"c#中的Main函数不能调同一个类中的非静态方法"

IntelliJ中的main函数和System.out.println()快捷键

1.在IntelJ中和Eclipse中稍有不同,在Eclipse中,输入main再按Alt+/即可自动补全main函数,但是在IntellJ中则是输入psvm,选中即可 2.在方法体内部有for循环,在IntellJ中是输入fori,然后会有一个提示,选中需要的for循环即可 3.System.out.println();在IntellJ中是输入sout

[转]go中的main函数和init函数

Go里面有两个保留的函数:init函数(能够应用于所有的package)和main函数(只能应用于package main).这两个函数在定义时不能有任何的参数和返回值.虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,我们都强烈建议用户在一个package中每个文件只写一个init函数. Go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数.每个package中的init函数都是可选的,但package main就必

在Android工程中运行main函数

在main函数中右键 --> Run As --> Run Configurations.. Java Application中的类 --> Classpath --> Bootstrap Entries --> Advanced... Add Library -- > OK JRE System Library --> Next default JRE --> Finish 将JRE Library up到最上方

eclipse中执行main函数添加参数

我们通常执行main函数是在类中右键选择Run As --> Java Application 但是如何执行时带有参数呢? Run As --> Run Configurations 打开如下界面

SQL Server中的RAND函数的介绍和区间随机数值函数的实现

    工作中会遇到SQL Server模拟数据生成以及数值列值(如整型.日期和时间数据类型)随机填充等等任务,这些任务中都要使用到随机数.鉴于此,本文将对SQL Server中随机数的使用简单做个总结 .   T-SQL 随机有关的三个函数          RAND([seed] 此函数生成从0到1之间随机 float 值(详细说明查看https://technet.microsoft.com/zh-cn/library/ms177610(v=sql.90).aspx). CHECKSUM

SQL SERVER 中的 object_id()函数

在数据库中有一个系统表sysobjects,里面存储了数据库各个对象的信息.可以查询下看看结果.可以看出每个对象都有一个ID,这个表存储了表,存储过程,触发器,视图等相关信息.注意:字段没有. object_id就是根据对象名称返回该对象的id.  object_name是根据对象id返回对象名称. select object_id(对象名)等同于:  select id from sysobjects where name=对象名 select object_name(id号)等同于:  se

sql server中的开窗函数over、视图、事物

一.开窗函数over的作用有两个: 1.排序order by,row_number,翻页 2.划区partition by,结合聚合函数针对某部分数据进行汇总 翻页的sql server 语句: select * from ( select *,row_number() over( order by id) as num from 表名) as t where t.num>6 and t.num<10 二.视图:用于存储一个select语句.并不是存储数据的一个容器. create view

java eclipse打jar包和执行jar中的main函数

jar包使用eclipse打包步骤 右键需要打包的项目->选择Export 到这里有两种打包的方式 1.如果项目中没有使用其他第三方包等,则直接选择下图中的第一种即可(JAR file) 2.如果项目中有引用其他第三方包等,则使用选择下图中的第二种(Runnable jar file) 本文介绍第二种 launch configuration:选择执行的main方法的类 Export destination:选择保存目录 Library handling:选择第三种,复制jar跟随:finish