redis整个程序的入口函数在server.c中的main函数,函数调用关系如下图1,调用顺序为从上到下,从左至右。
图1 redis启动函数调用图
main函数源码如下,1-55行根据配置文件和启动命令参数设置全局对象server ,57-59设置redis的服务器端为后台进程, initServer主要提前创建一些经常用到的对象用于节约内存,根据设置的ip地址和端口创建监听套接字用于客户端连接,并初始化时间事件,64行用于设置server->el ->beforesleep = beforesleep,aeMain函数是个循环函数,用于监听客户端连接,接收客户端命令并进行处理等。
1 //整个程序的入口函数 2 int main(int argc, char **argv) { 3 //初始化服务器配置,设置全局对象server的状态 4 initServerConfig(); 5 6 /* Store the executable path and arguments in a safe place in order 7 * to be able to restart the server later. */ 8 //存储运行命令的绝对路径及运行参数 9 server.executable = getAbsolutePath(argv[0]); 10 server.exec_argv = zmalloc(sizeof(char*)*(argc+1)); 11 server.exec_argv[argc] = NULL; 12 for (j = 0; j < argc; j++) server.exec_argv[j] = zstrdup(argv[j]); 13 14 if (argc >= 2) { 15 j = 1; /* First option to parse in argv[] */ 16 sds options = sdsempty(); 17 char *configfile = NULL; 18 19 //命令指定了配置文件,对配置文件做处理,配置文件跟在程序名后第一位 20 /* First argument is the config file name? */ 21 if (argv[j][0] != '-' || argv[j][1] != '-') { 22 configfile = argv[j]; 23 server.configfile = getAbsolutePath(configfile); 24 /* Replace the config file in server.exec_argv with 25 * its absolute path. */ 26 zfree(server.exec_argv[j]); 27 server.exec_argv[j] = zstrdup(server.configfile); 28 j++; 29 } 30 31 /* All the other options are parsed and conceptually appended to the 32 * configuration file. For instance --port 6380 will generate the 33 * string "port 6380\n" to be parsed after the actual file name 34 * is parsed, if any. */ 35 while(j != argc) {//解析除配置文件外的其它参数 36 if (argv[j][0] == '-' && argv[j][1] == '-') { 37 /* Option name */ 38 if (!strcmp(argv[j], "--check-rdb")) { 39 /* Argument has no options, need to skip for parsing. */ 40 j++; 41 continue; 42 } 43 if (sdslen(options)) options = sdscat(options,"\n"); 44 options = sdscat(options,argv[j]+2);//去掉参数前面的-- 45 options = sdscat(options," ");//参数对应的值和参数名应" "分隔 46 } else { 47 /* Option argument */ 48 options = sdscatrepr(options,argv[j],strlen(argv[j])); 49 options = sdscat(options," "); 50 } 51 j++; 52 } 53 //从指定配置文件和命令选项设置服务器对象server参数,覆盖默认配置 54 loadServerConfig(configfile,options); 55 } 56 57 server.supervised = redisIsSupervised(server.supervised_mode); 58 int background = server.daemonize && !server.supervised; 59 if (background) daemonize();//后台进程模式 60 61 //初始化服务器功能,包括时间事件1ms调用serverCron,文件事件(套接字可读可写时的处理函数),集群初始化等 62 initServer(); 63 aeSetBeforeSleepProc(server.el,beforeSleep);//设置beforeSleep事件处理函数 64 aeSetAfterSleepProc(server.el,afterSleep);//设置aftersleep事件处理函数 65 aeMain(server.el);//循环,接受客户端连接,处理命令等 66 aeDeleteEventLoop(server.el);//退出循环,删除事件处理 67 return 0; 68 }
initServer函数源码如下,主要对server中的变量进行初始化,其中listenToPort根据监听的地址和端口, 设置server.ipfd(监听套接字数组)和server.ipfd_count(监听套接字数目),85行aeCreateTimeEvent设置定时器事件,每1ms执行serverCron函数,94行aeCreateFileEvent函数设置监听套接字有客户端连接时执行的事件处理函数acceptTcpHandler。
1 void initServer(void) { 2 int j; 3 server.hz = server.config_hz; 4 server.pid = getpid(); 5 server.current_client = NULL; 6 server.clients = listCreate(); 7 server.clients_index = raxNew(); 8 server.clients_to_close = listCreate(); 9 server.slaves = li