標籤:blog io os ar for strong sp div on
找了半天,發覺還是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);#endifsetlocale(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 1FILE *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多線程網路編程》介紹過
好像Google的glog好像效能不錯,什麼時候有時間把glog看完 再來討論log日誌的實現
redis 學習筆記一