关于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的启动流程大概分析完毕,中间不少地方只是大概说明了函数的做用,具体作法留待以后详细分析。