版本:3.0.7
源码:redis.c
/*
* main函数
*/
int main(int argc, char **argv) {
struct timeval tv;
/* We need to initialize our libraries, and the server configuration. */
// 初始化库
#ifdef INIT_SETPROCTITLE_REPLACEMENT
spt_init(argc, argv);
#endif
setlocale(LC_COLLATE,"");
zmalloc_enable_thread_safeness();
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
srand(time(NULL)^getpid());
gettimeofday(&tv,NULL);
dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
// 检查是否sentinel模式
server.sentinel_mode = checkForSentinelMode(argc,argv);
// 初始化服务器配置项
initServerConfig();
// 初始化sentinel
/* 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();
}
if (argc >= 2) {
int j = 1; /* First option to parse in argv[] */
sds options = sdsempty();
char *configfile = NULL;
// 处理-v/--version/-h/--help/--test-memory
/* 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 (server.sentinel_mode && configfile && *configfile == '-') {
redisLog(REDIS_WARNING,
"Sentinel config from STDIN not allowed.");
redisLog(REDIS_WARNING,
"Sentinel needs config file on disk to save state. Exiting...");
exit(1);
}
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");
}
// 守护模式
if (server.daemonize) daemonize();
// 初始化服务器
initServer();
// 守护模式创建PID文件
if (server.daemonize) createPidFile();
// 进程环境相关设置,设置名字
redisSetProcTitle(argv[0]);
// 打印 ASCII LOGO
redisAsciiArt();
// 检查内核backlog参数支持
checkTcpBacklogSettings();
if (!server.sentinel_mode) {
/* Things not needed when running in Sentinel mode. */
// 打印服务器启动成功
redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
// 检查内存分配策略,并打印警告
linuxMemoryWarnings();
#endif
// 加载持久化文件
loadDataFromDisk();
// 如果是集群模式,检查加载的数据是否正确
if (server.cluster_enabled) {
if (verifyClusterConfigWithData() == REDIS_ERR) {
redisLog(REDIS_WARNING,
"You can't have keys in a DB different than DB 0 when in "
"Cluster mode. Exiting.");
exit(1);
}
}
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 {
// sentnel模式启动
sentinelIsRunning();
}
// 设置的max内存小于1MB,提醒用户
/* 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);
}
// 设置事件处理之前需要调用的函数
aeSetBeforeSleepProc(server.el,beforeSleep);
// 开始事件循环
/*
* 调用链举例
* initServer():aeCreateFileEvent注册acceptTcpHandler事件
* 当客户端请求来临时:acceptTcpHandler->anetTcpAccept
* 连接建立成功后,创建客户端:acceptCommonHandler->createClient
* 创建客户端成功后:aeCreateFileEvent注册readQueryFromClient
* 执行客户端命令:readQueryFromClient->processInputBuffer->processCommand
* 在processCommand中通过lookupCommand找到命令对应的执行函数设置c->cmd,在一系列的判断后,调用call函数执行命令
* call函数会执行c->cmd->proc(c):在proc中会执行,比如proc是getCommand,在getCommand中会执行addReply函数
* addReply函数是返回输出给客户端的底层调用,此函数会增加写事件sendReplyToClient到多路复用库中
* 关于事件管理:每次执行1000次接收连接(acceptTcpHandler),每次将所有的就绪事件都处理了(aeApiPoll函数)
*/
aeMain(server.el);
// 服务器关闭,停止事件循环
aeDeleteEventLoop(server.el);
return 0;
}
原文出自:http://blog.youkuaiyun.com/daiyudong2020/article/details/54237076