關於Redis的源碼分析,在Redis設計與實現
中分析的至關清楚,本人在學習的過程當中也是閱讀此書。linux
咱們從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。這三個值分別表明的意義是:
若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的啓動流程大概分析完畢,中間不少地方只是大概說明了函數的做用,具體作法留待以後詳細分析。