找了半天,發覺還是redis的源碼看起來比較舒服。所以決定今年把redis的源碼讀一遍順便做個讀書筆記。好好記錄下。話說現在越來不越不願意用腦袋來記錄東西,喜歡靠note來記。話說這樣不愛用腦會不會過早的老年痴呆呢~~~
一、redis下載編譯
這里沒什么好說的
用的版本是redis-2.8.17
1)redis-server是可執行程序
2)mian函數在redis.c里面
3)如果要修改調試 這屆在src目錄下 修改后make或者make clean;make 就行
從main函數說起這里先說兩個部分一個是 redis里面的回調函數 還有一個是redis里面的log日志
二、redis里的回調函數
先看下代碼;這是把redis里面的回調函數拿出來修改下
/*
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函數的
假如注冊了回調函數的情況下,則調用的是 注冊了的回調函數
int main(void)
{
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
zmalloc_oom_handler(10);
getchar();
return 0;
}
運行結果
zmalloc_set_oom_handler
redisOutOfMemoryHandler----------:10
現在看看redis的代碼
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();
..........
}
zmalloc_set_oom_handler注冊回調函數
redisOutOfMemoryHandler主要是個log日志打印,即在內存分配失敗的時候觸發回調函數,打印log。
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 單是大抵就是這樣
#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日志的實現
