? ? ? ? ? 時間過的很快,經過快1個月的時間學習,本人對Redis源代碼的分析已經超過了一半,上幾次的學習,我主要的是對于Redis工具類的代碼進行了學習。后面的幾天我將會學習Redis代碼中的一些封裝類的實現,這些封裝類在整個Redis系統中都可能普遍用到。比如說我馬上要分析的在zmalloc的內存封裝的實現。先拋開Redis的內存函數庫不說,在純粹的C語言中,內存分配的函數有malloc,free,relloc這3個函數,熟悉C語言編程的同學一定不會陌生。但是在這里Redis代碼的編寫者,在Redis系統中對內存的分配又做了一次小小封裝。我也只能說是一個小小的封裝,核心的調用方法仍是C語言中的這3個函數。先看一下在zmalloc.h頭文件中define的一些API:
~~~
void *zmalloc(size_t size); /* 調用zmalloc申請size個大小的空間 */
void *zcalloc(size_t size); /* 調用系統函數calloc函數申請空間 */
void *zrealloc(void *ptr, size_t size); /* 原內存重新調整空間為size的大小 */
void zfree(void *ptr); /* 釋放空間方法,并更新used_memory的值 */
char *zstrdup(const char *s); /* 字符串復制方法 */
size_t zmalloc_used_memory(void); /* 獲取當前已經占用的內存大小 */
void zmalloc_enable_thread_safeness(void); /* 是否設置線程安全模式 */
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)); /* 可自定義設置內存溢出的處理方法 */
float zmalloc_get_fragmentation_ratio(size_t rss); /* 所給大小與已使用內存大小之比 */
size_t zmalloc_get_rss(void);
size_t zmalloc_get_private_dirty(void); /* 獲取私有的臟數據大小 */
void zlibc_free(void *ptr); /* 原始系統free釋放方法 */
~~~
在這里還要介紹幾個概念和變量:
~~~
static size_t used_memory = 0;
static int zmalloc_thread_safe = 0;
pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
~~~
第一個used_memory,看意思我們也知道這是系統已經使用了多少的內存大小,在全局只維護了這么一個變量的大小,說明作者希望根據此來分析出當前的內存的使用情況,第二個zmalloc_thread_safe,這指的是線程安全模式狀態,下面的mutext,就是為此服務端,這個在操作系統中就出現過。據此,我們大概知道了,Redis在代碼的malloc等操作的時候,會根據創建的大小,會更新used_memory,并操作的模式會有線程安全和不安去模式的區分。
~~~
/* 在對內存空間做使用的時候,進行了加鎖控制 */
#define update_zmalloc_stat_add(__n) do { \
pthread_mutex_lock(&used_memory_mutex); \
used_memory += (__n); \
pthread_mutex_unlock(&used_memory_mutex); \
} while(0)
~~~
上面的函數操作就是線程安全模式時候的一個操作,通過鎖操作實現對于used_memory的控制,是對這個變量做控制,避免了這個數值出現臟數據的可能。
~~~
/* 調用zmalloc申請size個大小的空間 */
void *zmalloc(size_t size) {
//實際調用的還是malloc函數
void *ptr = malloc(size+PREFIX_SIZE);
//如果申請的結果為null,說明發生了oom,調用oom的處理方法
if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
//更新used_memory的大小
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
}
~~~
zmalloc的具體實現,調用的還是malloc的C語言方法,做了OOM的異常處理,然后更新大小。在update_zmalloc_stat_alloc方法里面:
~~~
/* 申請新的_n大小的內存,分為線程安全,和線程不安全的模式 */
#define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
if (zmalloc_thread_safe) { \
update_zmalloc_stat_add(_n); \
} else { \
used_memory += _n; \
} \
} while(0)
~~~
同理zfree()的操作就是上面的反操作,調用free方法,把used_memory的值,做減少操作。在APIL里面還出現了zcalloc方法,下面函數代碼中我分析一下這個函數和malloc到底有什么不同呢:
~~~
/* 調用系統函數calloc函數申請空間 */
void *zcalloc(size_t size) {
//calloc與malloc的意思一樣,不過參數不一樣
//void *calloc(size_t numElements,size_t sizeOfElement),;numElements * sizeOfElement才是最終的內存的大小
//所在這里就是申請一塊大小為size+PREFIX_SIZE的空間
void *ptr = calloc(1, 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
}
~~~
在這些方法中,作者很人性化的開放了一些API給用戶調用,比如說為了效率的提高,可以不開啟安全模式啊;
~~~
/* 是否設置線程安全模式 */
void zmalloc_enable_thread_safeness(void) {
zmalloc_thread_safe = 1;
}
~~~
或者自定義一個更合理的內存溢出的處理函數,更滿足系統的需要:
~~~
/* 可自定義設置內存溢出的處理方法 */
void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
zmalloc_oom_handler = oom_handler;
}
~~~
- 前言
- (一)--Redis結構解析
- (二)--結構體分析(1)
- (三)---dict哈希結構
- (四)-- sds字符串
- (五)--- sparkline微線圖
- (六)--- ziplist壓縮列表
- (七)--- zipmap壓縮圖
- (八)--- t_hash哈希轉換
- (九)--- t_list,t_string的分析
- (十)--- testhelp.h小型測試框架和redis-check-aof.c日志檢測
- (十一)--- memtest內存檢測
- (十二)--- redis-check-dump本地數據庫檢測
- (十三)--- redis-benchmark性能測試
- (十四)--- rdb.c本地數據庫操作
- (十五)--- aof-append only file解析
- (十六)--- config配置文件
- (十七)--- multi事務操作
- (十八)--- db.c內存數據庫操作
- (十九)--- replication主從數據復制的實現
- (二十)--- ae事件驅動
- (二十一)--- anet網絡通信的封裝
- (二十二)--- networking網絡協議傳輸
- (二十三)--- CRC循環冗余算法和RAND隨機數算法
- (二十四)--- tool工具類(2)
- (二十五)--- zmalloc內存分配實現
- (二十六)--- slowLog和hyperloglog
- (二十七)--- rio系統I/O的封裝
- (二十八)--- object創建和釋放redisObject對象
- (二十九)--- bio后臺I/O服務的實現
- (三十)--- pubsub發布訂閱模式
- (三十一)--- latency延遲分析處理
- (三十二)--- redis-cli.c客戶端命令行接口的實現(1)
- (三十三)--- redis-cli.c客戶端命令行接口的實現(2)
- (三十四)--- redis.h服務端的實現分析(1)
- (三十五)--- redis.c服務端的實現分析(2)
- (三十六)--- Redis中的11大優秀設計