redis的入口函数在redis.c 中的main函数中
int main(int argc, char **argv) {
// 初始化服务器配置,主要用于给struct redisServer server; 赋值,并实现一些基本的命令。,并监听clint的连接信息。
initServerConfig();
// 从这里可以看到可以通过-v 或者 --version 来得到当前redis的版本信息
// 如果在启动时添加--test-memory,则会调用memtest来对内存进行测试
//
if (argc >= 2) {
int j = 1; /* First option to parse in argv[] */
sds options = sdsempty();
char *configfile = NULL;
/* Handle special options --help and --version */
// 处理特殊选项 -h 、-v 和 --test-memory
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);
}
}
// 创建为守护进程
if (server.daemonize) daemonize();
// 创建并初始化服务器数据结构
initServer();
// 如果服务器是守护进程,那么创建 PID 文件
if (server.daemonize) createPidFile();
// 服务端打印的 ASCII LOGO
redisAsciiArt();
// 如果服务器不是运行在 SENTINEL 模式,其中sentinel 是server的另外一种模式,主要用于监控复制server
if (!server.sentinel_mode) {
/* Things not needed when running in Sentinel mode. */
// 可见正常情况下redis 会打印当前的版本信心
redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
#ifdef __linux__
// 这个值vm.overcommit_memory 设为零会有警告,一般发行版设置为1
linuxOvercommitMemoryWarning();
#endif
/* Warning the user about suspicious maxmemory setting. */
// 如果server 只能使用1M的memory则报错,从这里可以知道只要设置大于1M就不会报错,也就是redis server最小只要2M的就可以运行 ?
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);
}
return 0;
}
我们首先看看initServerConfig 中基本的命令如下:
server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi");
server.lpushCommand = lookupCommandByCString("lpush");
server.lpopCommand = lookupCommandByCString("lpop");
server.rpopCommand = lookupCommandByCString("rpop");
同时设置慢日志的参数
/* Slow log */
// 初始化慢查询日志
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
redis中的memtest 函数是在memtest.c中实现的
void memtest(size_t megabytes, int passes) {
if (ioctl(1, TIOCGWINSZ, &ws) == -1) {
ws.ws_col = 80;
ws.ws_row = 20;
}
// memtest的具体实现
memtest_test(megabytes,passes);
//可以看到除了redis提供的memtest如果成的话,会打印"Your memory passed this test.".用户在redis提供的memtest测试通过外,还可以继续采用memtest86和memtester 这两个tool来测试内存。
printf("\nYour memory passed this test.\n");
printf("Please if you are still in doubt use the following two tools:\n");
printf("1) memtest86: http://www.memtest86.com/\n");
printf("2) memtester: http://pyropus.ca/software/memtester/\n");
exit(0);
}
从memtest_test的实现可以知道redis中的memtest采用了四种测试,分别是Addressing test/Random fill/Solid fill/Checkerboard fill
void memtest_test(size_t megabytes, int passes) {
size_t bytes = megabytes*1024*1024;
unsigned long *m = malloc(bytes);
int pass = 0;
if (m == NULL) {
fprintf(stderr,"Unable to allocate %zu megabytes: %s",
megabytes, strerror(errno));
exit(1);
}
while (pass != passes) {
pass++;
memtest_progress_start("Addressing test",pass);
memtest_addressing(m,bytes);
memtest_progress_end();
memtest_progress_start("Random fill",pass);
memtest_fill_random(m,bytes);
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
memtest_progress_start("Solid fill",pass);
memtest_fill_value(m,bytes,0,(unsigned long)-1,'S');
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
memtest_progress_start("Checkerboard fill",pass);
memtest_fill_value(m,bytes,ULONG_ONEZERO,ULONG_ZEROONE,'C');
memtest_progress_end();
memtest_compare_times(m,bytes,pass,4);
}
}
我们以Addressing test 为例
void memtest_addressing(unsigned long *l, size_t bytes) {
//可见l表示要测试的起始地址,bytes表示要测试的长度
unsigned long words = bytes/sizeof(unsigned long);
unsigned long j, *p;
/* Fill */
p = l;
// 往起始地址里面写的值就是指针本身的地址例如*123456=123456
for (j = 0; j < words; j++) {
*p = (unsigned long)p;
p++;
if ((j & 0xffff) == 0) memtest_progress_step(j,words*2,'A');
}
/* Test */
p = l;
#遍历检查指针的地址和值是否相等。
for (j = 0; j < words; j++) {
if (*p != (unsigned long)p) {
printf("\n*** MEMORY ADDRESSING ERROR: %p contains %lu\n",
(void*) p, *p);
exit(1);
}
p++;
if ((j & 0xffff) == 0) memtest_progress_step(j+words,words*2,'A');
}
}
我们继续看看redis是如何实现守护进程的
void daemonize(void) {
int fd;
if (fork() != 0) exit(0); /* parent exits */
setsid(); /* create a new session */
/* Every output goes to /dev/null. If Redis is daemonized but
* the 'logfile' is set to 'stdout' in the configuration file
* it will not log at all. */
if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO) close(fd);
}
}
原来是通过fork处一个子进程并重启一个新的回话session 从而实现守护进程
最后我们看看redis-server 如果打印log的
void redisAsciiArt(void) {
//原来log是保存在头文件中
#include "asciilogo.h"
char *buf = zmalloc(1024*16);
char *mode = "stand alone";
if (server.cluster_enabled) mode = "cluster";
else if (server.sentinel_mode) mode = "sentinel";
// 将要打印的log和redis version等信息保存在buf中
snprintf(buf,1024*16,ascii_logo,
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
(sizeof(long) == 8) ? "64" : "32",
mode, server.port,
(long) getpid()
);
//最终通过这个函数打印log
redisLogRaw(REDIS_NOTICE|REDIS_LOG_RAW,buf);
zfree(buf);
}
void redisLogRaw(int level, const char *msg) {
const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
const char *c = ".-*#";
FILE *fp;
char buf[64];
int rawmode = (level & REDIS_LOG_RAW);
int log_to_stdout = server.logfile[0] == '\0';
level &= 0xff; /* clear flags */
if (level < server.verbosity) return;
//fp表示log要输出到console上还是文件中,可见不能同时输出到这两个地方
fp = log_to_stdout ? stdout : fopen(server.logfile,"a");
if (!fp) return;
if (rawmode) {
fprintf(fp,"%s",msg);
} else {
int off;
struct timeval tv;
gettimeofday(&tv,NULL);
off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec));
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg);
}
// 最终通过fprintf来打印,并通过fflush来刷新.注意这里没有用printf来打印
fflush(fp);
if (!log_to_stdout) fclose(fp);
// 可见logo信息会输出到syslog中
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
}
要输出的redis log如下:也就是asciilogo.h中的内容.
char *ascii_logo =
" _._ \n"
" _.-``__ ''-._ \n"
" _.-`` `. `_. ''-._ Redis %s (%s/%d) %s bit\n"
" .-`` .-```. ```\\/ _.,_ ''-._ \n"
" ( ' , .-` | `, ) Running in %s mode\n"
" |`-._`-...-` __...-.``-._|'` _.-'| Port: %d\n"
" | `-._ `._ / _.-' | PID: %ld\n"
" `-._ `-._ `-./ _.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | http://redis.io \n"
" `-._ `-._`-.__.-'_.-' _.-' \n"
" |`-._`-._ `-.__.-' _.-'_.-'| \n"
" | `-._`-._ _.-'_.-' | \n"
" `-._ `-._`-.__.-'_.-' _.-' \n"
" `-._ `-.__.-' _.-' \n"
" `-._ _.-' \n"
" `-.__.-' \n\n";
redis的main函数
最新推荐文章于 2024-07-26 17:37:52 发布