Redis學習筆記(二)

關於Redis的源碼分析,在Redis設計與實現中分析的至關清楚,本人在學習的過程當中也是閱讀此書。linux

Redis-server中的MAIN函數

咱們從redis的main函數着手,來分析整個redis的啓動過程,main函數在redis.c/redis.h中的第2977行
lang:c #ifdef INIT_SETPROCTITLE_REPLACEMENT spt_init(argc, argv); #endif 此處用來修改進程名redis

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內存溢出以後記錄報警日誌,而後退出整個程序。app

dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());

函數存在於dict.c中的第90行,使用gettimeofday(&tv,NULL);所取到的精確時間和當前進程的pid異或來生成DICT中的哈希函數的種子。less

server.sentinel_mode = checkForSentinelMode(argc,argv);
    ...
    if (server.sentinel_mode) {
        initSentinelConfig();
        initSentinel();
    }

根據輸入的參數判斷是否以sentinel模式運行,Redis-sentinel是Redis的集羣管理工具,主要是中心節點用來監控其餘節點的工做狀況並進行故障恢復。socket

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行。oop

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、一、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;

若是事件輪詢結束,釋放以前申請的資源,而且退出函數。

至此,整個Redis的啓動流程大概分析完畢,中間不少地方只是大概說明了函數的做用,具體作法留待以後詳細分析。

相關文章
相關標籤/搜索