码迷,mamicode.com
首页 > 其他好文 > 详细

Redis-server中的MAIN函数

时间:2015-05-19 18:34:49      阅读:380      评论:0      收藏:0      [点我收藏+]

标签:

我们从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

Redis-server中的MAIN函数

标签:

原文地址:http://www.cnblogs.com/CRXY/p/4514988.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!