最近一直在看redis的源码,准备把对源码的理解以及阅读心得记录下来,避免忘记并可以和大家分享、谈论。看代码的思路很简单,直接从main函数走起,先看看初始化过程。
redis中一个最重要的数据结构是redis_server,会创建一个这个结构的全局变量server,表示当前redis的配置及状态,初始化的大部分工作就是设置这个结构的属性。
可以把初始化工作主要划分为4个部分:
1)为server设置默认值
2)解析命令行参数
3)解析配置文件
4)初始化server
初始化完成后,就会启动事件循环,以接收、服务请求。下面分步骤解析main函数,不会关注sentinel逻辑。
(1)首先是main函数的起始部分,主要进行简单的初始化工作,包括设置collate,设置随机数种子,然后调用initServerConfig()为全局变量server设置默认值。
struct timeval tv;
/* We need to initialize our libraries, and the server configuration. */
#ifdef INIT_SETPROCTITLE_REPLACEMENT
spt_init(argc, argv);
#endif
// <MM>
// 使用环境变量初始化 字符串比较
// </MM>
setlocale(LC_COLLATE,"");
zmalloc_enable_thread_safeness();
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
// <MM>
// 设置random的种子,之后会生成随机的run id,所有加入进程id因素
// </MM>
srand(time(NULL)^getpid());
gettimeofday(&tv,NULL);
dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
server.sentinel_mode = checkForSentinelMode(argc,argv);
// <MM>
// 为redisServer设置初始默认值
// </MM>
initServerConfig();
/* We need to init sentinel right now as parsing the configuration file
* in sentinel mode will have the effect of populating the sentinel
* data structures with master nodes to monitor. */
if (server.sentinel_mode) {
initSentinelConfig();
initSentinel();
}
在initServerConfig函数中,大部分是对server的属性设置默认值,还有一部分是调用populateCommandTable函数对redis的命令表初始化。全局变量redisCommandTable是redisCommand类型的数组,保存redis支持的所有命令。server.commands是一个dict,保存命令名到redisCommand的映射。populateCommandTable函数会遍历全局redisCommandTable表,把每条命令插入到server.commands中,根据每个命令的属性设置其flags。
redisCommand结构如下:
struct redisCommand {
// 命令名称,在server.commands命令表中,以命令名位key
char *name;
// 命令处理函数
r