一、redis下载编译
这里没什么好说的
用的版本是redis-2.8.17
1)redis-server是可执行程序
2)mian函数在redis.c里面
3)如果要修改调试 这届在src目录下 修改后make或者make clean;make 就行
从main函数说起这里先说两个部分一个是 redis里面的回调函数 还有一个是redis里面的log日志
二、redis里的回调函数
先看下代码;这是把redis里面的回调函数拿出来修改下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/*
redis里的回调函数
*/
#include<stdio.h>
#include<stdlib.h>
static
void
zmalloc_default_oom(
size_t
size)
{
printf
(
"zmalloc_default_oom\n"
);
fprintf
(stderr,
"zmalloc: Out of memory trying to allocate %d bytes\n"
,size);
fflush
(stderr);
}
static
void
(*zmalloc_oom_handler)(
size_t
) = zmalloc_default_oom;
void
zmalloc_set_oom_handler(
void
(*oom_handler)(
size_t
))
{
printf
(
"zmalloc_set_oom_handler\n"
);
zmalloc_oom_handler = oom_handler;
}
void
redisOutOfMemoryHandler(
size_t
allocation_size)
{
printf
(
"redisOutOfMemoryHandler------:%d\n"
,allocation_size);
}
int
main(
void
)
{
//zmalloc_set_oom_handler(redisOutOfMemoryHandler);
zmalloc_oom_handler(10);
getchar
();
return
0;
}
|
运行结果
zmalloc_default_oom
zmalloc:Out of memory trying to allocate 10 bytes
我们可以看到默认情况下,在没有注册回调函数的情况下zmalloc_oom_handler是指向 zmalloc_default_oom函数的
假如注册了回调函数的情况下,则调用的是 注册了的回调函数
1
2
3
4
5
6
7
|
int
main(
void
)
{
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
zmalloc_oom_handler(10);
getchar
();
return
0;
}
|
运行结果
zmalloc_set_oom_handler
redisOutOfMemoryHandler----------:10
现在看看redis的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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()
开启了内存分配管理的线程安全变量,当内存分配时,
redis会统计一个总内存分配量,这是一个共享资源,
所以需要原子性操作,在redis的内存分配代码里,
当需要原子操作时,就需要打开线程安全变量。
*/
zmalloc_enable_thread_safeness();
/*
zmalloc_set_oom_handler()
是一个内存分配错误处理,
当无法得到需要的内存量时,
会调用redisOutOfMemoryHandler函数。
*/
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
srand
(
time
(NULL)^getpid());
gettimeofday(&tv,NULL);
dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
server.sentinel_mode = checkForSentinelMode(argc,argv);
initServerConfig();
..........
}
|
1
|
zmalloc_set_oom_handler注册回调函数
|
1
|
redisOutOfMemoryHandler主要是个
log
日志打印,即在内存分配失败的时候触发回调函数,打印
log
。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void
*zmalloc(
size_t
size) {
void
*ptr =
malloc
(size+PREFIX_SIZE);
if
(!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return
ptr;
#else
*((
size_t
*)ptr) = size;
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return
(
char
*)ptr+PREFIX_SIZE;
#endif
}
|
在分配内存失败的时候,触发回调函数
三、redis的log日志
由于redis是单线程的 所以在redis.c里面的log没有做成多线程
这样的log,在单线程下 速度很快,因为无锁。但是在多线程下是不安全
简化了下 redis的log 单是大抵就是这样
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
/* Log levels */
#define REDIS_DEBUG 0
#define REDIS_VERBOSE 1
#define REDIS_NOTICE 2
#define REDIS_WARNING 3
#define REDIS_MAX_LOGMSG_LEN 1024 /* 默认信息长度 */
void
redisLogRaw(
int
level,
const
char
*msg);
void
redisLog(
int
level,
const
char
*fmt, ...);
/*
verbosity表示开启log的级别
需要写log的时候,log级别小于等于verbosity写log
否则不会写log
*/
struct
redisServer {
int
verbosity;
/* 日志级别*/
char
*logfile;
/* Path of log file */
};
struct
redisServer server;
/* server global state */
void
redisLog(
int
level,
const
char
*fmt, ...)
{
//如果level级别大于verbosity则不打印
if
(level> server.verbosity)
{
return
;
}
va_list
ap;
char
msg[REDIS_MAX_LOGMSG_LEN];
va_start
(ap, fmt);
vsnprintf(msg,
sizeof
(msg), fmt, ap);
va_end
(ap);
redisLogRaw(level,msg);
}
void
redisLogRaw(
int
level,
const
char
*msg)
{
#if 1
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;
if
(server.logfile != NULL)
{
fp=
fopen
(server.logfile,
"a"
);
}
else
{
fp=stdout;
}
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
(fp,
" %s\n"
,msg);
fflush
(fp);
if
(server.logfile != NULL)
{
fclose
(fp);
}
#endif
}
int
main(
void
)
{
server.verbosity=2;
server.logfile=NULL;
redisLog(1,
"11111111\n"
);
redisLog(2,
"22222\n"
);
redisLog(3,
"333\n"
);
getchar
();
return
0;
}
|
关于log日志 怎么在不影响性能的情况下 最快最多的 写日志能,
多线程用锁的话必然会影响速度
用双队列这种方式好像挺不错,可以把log的内容的放到队列里,一个队列负责接收log,一个队列负责打印log
打印完的log队列,然后跟接收log队列互换,在继续 这种方法陈硕的 《linux多线程网络编程》介绍过
好像谷歌的glog好像性能不错,什么时候有时间把glog看完 再来讨论log日志的实现