启动过程大体可以分为5个阶段
基本初始化
- 设置server运行时区
- 设置hash函数的随机种子
... // 设置时区 setlocale(LC_COLLATE,""); tzset(); /* Populates 'timezone' global. */ zmalloc_set_oom_handler(redisOutOfMemoryHandler); srand(time(NULL)^getpid()); gettimeofday(&tv,NULL); // 设置随机种子 char hashseed[16]; getRandomHexChars(hashseed,sizeof(hashseed)); dictSetHashFunctionSeed((uint8_t*)hashseed); ...
检查哨兵模式,并检查是否要执行 RDB 检测或 AOF 检查
... // 检查是否是哨兵模式 server.sentinel_mode = checkForSentinelMode(argc,argv); // 初始化server的配置 initServerConfig(); moduleInitModulesSystem(); ... // 如果是哨兵模式,会再次进行哨兵模式的初始化 if (server.sentinel_mode) { initSentinelConfig(); initSentinel(); } // 检查是否要执行 RDB 检测或 AOF 检查 /* Check if we need to start in redis-check-rdb/aof mode. We just execute * the program main. However the program is part of the Redis executable * so that we can easily execute an RDB check on loading errors. **/ if (strstr(argv[0],"redis-check-rdb") != NULL) redis_check_rdb_main(argc,argv,NULL); else if (strstr(argv[0],"redis-check-aof") != NULL) redis_check_aof_main(argc,argv);
运行参数解析
... // 参数为两个时,进行一些特殊处理 /* 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? */ // 比如说这样启动:redis-server ./redis.conf // ./redis.conf 其实就是配置文件的路径 if (argv[j][0] != '-' || argv[j][1] != '-') { configfile = argv[j]; server.configfile = getAbsolutePath(configfile); /* Replace the config file in server.exec_argv with * its absolute path. */ zfree(server.exec_argv[j]); server.exec_argv[j] = zstrdup(server.configfile); j++; } // 继续解析命令行参数 ... // 启动中打印日志 serverLog(LL_WARNING, "oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo"); serverLog(LL_WARNING, "Redis version=%s, bits=%d, commit=%s, modified=%d, pid=%d, just started", REDIS_VERSION, (sizeof(long) == 8) ? 64 : 32, redisGitSHA1(), strtol(redisGitDirty(),NULL,10) > 0, (int)getpid()); // 手动启动redis时,我们经常会看到的一些日志 if (argc == 1) { serverLog(LL_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"); } else { serverLog(LL_WARNING, "Configuration loaded"); } // redis-server是否后台运行, 支持的模式, 从配置中,我们可以看到redis支持的有以下几种 # If you run Redis from upstart or systemd, Redis can interact with your # supervision tree. Options: # supervised no - no supervision interaction # supervised upstart - signal upstart by putting Redis into SIGSTOP mode # supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET # supervised auto - detect upstart or systemd method based on # UPSTART_JOB or NOTIFY_SOCKET environment variables # Note: these supervision methods only signal "process is ready." # They do not enable continuous liveness pings back to your supervisor. // 但是需要注意的是,server.daemonize和server.supervised看起来是无法同时满足的 server.supervised = redisIsSupervised(server.supervised_mode); int background = server.daemonize && !server.supervised; if (background) daemonize();
至此,redis的配置文件加载就完毕了,接下来开始初始化server
初始化server
initServer(); ... 这段代码设置了两个信号的处理方式。 首先,signal(SIGHUP, SIG_IGN) 表示忽略 SIGHUP 信号。SIGHUP 信号通常在终端关闭或进程被挂断时发送给进程,设置为忽略后,进程不会因终端关闭而退出。 其次,signal(SIGPIPE, SIG_IGN) 表示忽略 SIGPIPE 信号。SIGPIPE 信号在进程向一个已关闭的套接字或无效的地址发送数据时产生,设置为忽略后,进程不会因写入无效套接字而退出。 综上所述,这段代码的作用是使进程在终端关闭或写入无效套接字时不会退出。 signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); ... // 接下来还是对server进行了一些初始化赋值操作 ... // 获取当前进程的pid server.pid = getpid(); ...
// 设置进程的名称
redisSetProcTitle(argv[0]);
Redis server 在完成运行参数设置和初始化后,就可以开始处理客户端请求了。为了能持续地处理并发的客户端请求,server 在 main 函数的最后,会进入事件驱动循环机制。- 执行事件驱动框架
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。