标签:
我们从redis的main函数着手,来分析整个redis的启动过程,main函数在redis.c/redis.h中的第2977行
#ifdef INIT_SETPROCTITLE_REPLACEMENT
spt_init(argc, argv);
#endif
此处用来修改进程名
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内存溢出之后记录报警日志,然后退出整个程序。
dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
函数存在于dict.c中的第90行,使用gettimeofday(&tv,NULL);所取到的精确时间和当前进程的pid异或来生成DICT中的哈希函数的种子。
server.sentinel_mode = checkForSentinelMode(argc,argv);
...
if (server.sentinel_mode) {
initSentinelConfig();
initSentinel();
}
根据输入的参数判断是否以sentinel模式运行,Redis-sentinel是Redis的集群管理工具,主要是中心节点用来监控其他节点的工作情况并进行故障恢复。
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行。
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、1、2。这三个值分别代表的意义是:
0表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。也就是说该模式下将允许轻微的overcommit,而后果严重的将失败。
1表示内核允许分配所有的物理内存,而不管当前的内存状态如何。也就是说无论怎样都会给程序分配新的内存空间。
2表示内核将根据某个值来决定是否分配内存,这个值为swap+某个比例(默认值为50%)下的RAM。超过该值得内存分配申请都将失败。
若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;
如果事件轮询结束,释放之前申请的资源,并且退出函数。
更多精彩内容请关注:http://bbs.superwu.cn
关注超人学院微信:BJ-CRXY
标签:
原文地址:http://www.cnblogs.com/CRXY/p/4514988.html