结合redis设计与实现的redis源码学习-1-内存分配(zmalloc)
来源:互联网 发布:保法止效果知乎 编辑:程序博客网 时间:2024/05/23 00:06
在进入公司后的第一个任务就是使用redis的缓存功能实现服务器的云托管功能,在了解了大致需求后,依靠之前对redis的了解封装了常用的redis命令,并使用单例的连接池来维护与redis的连接,使用连接池来获取redis的连接对象,依靠这些功能基本可以实现要求的命令托管和任务调度功能。
在开始做这个需求的同时,我开始看redis设计与实现这本书,两个月将书看完了,对redis的数据结构和各种功能的逻辑有了一定的了解,为了加深对redis的认识,从今天开始结合redis设计与实现这本书来对redis的源码进行学习,因为这本书是基于redis2.9和3.0来编写的,所以此次我选择redis3.2源码进行学习,平台为linux。
对于redis的代码结构,可以去:Android路上的人(http://blog.csdn.net/Androidlushangderen)redis源码分析(一)查看,在熟悉了redis的设计后其实对源码从哪入手并没有头绪,感谢他梳理出来的redis代码结构,给了我一些灵感。本次我先从redis的数据结构实现来学习redis的源码。
下面我们开始redis源码的学习,为了方便查看,使用source insight进行代码管理:
redis中的内存分配是由zmalloc来实现的,在.h文件中,定义了多种mailloc的方式,jemalloc,tcmalloc和c库的malloc和其他平台,在此只针对c库的malloc进行分析,我删掉了一些兼容其他平台的代码:
/* zmalloc - total amount of allocated memory aware version of malloc()*/#ifndef __ZMALLOC_H#define __ZMALLOC_H/* Double expansion needed for stringification of macro values. */#define __xstr(s) __str(s)#define __str(s) #s#ifndef ZMALLOC_LIB#define ZMALLOC_LIB "libc"#endifvoid *zmalloc(size_t size);void *zcalloc(size_t size);void *zrealloc(void *ptr, size_t size);void zfree(void *ptr);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);size_t zmalloc_get_smap_bytes_by_field(char *field);size_t zmalloc_get_memory_size(void);void zlibc_free(void *ptr);#endif /* __ZMALLOC_H */
下面是.c文件
#include <stdio.h>#include <stdlib.h>/* This function provide us access to the original libc free(). */void zlibc_free(void *ptr) { free(ptr);}#include <string.h>#include <pthread.h>#include "config.h" //redis的配置文件,有很多宏定义决定代码选择#include "zmalloc.h"//加锁修改已经使用的内存大小,用宏定义来实现函数,do{}while(0)来保证使用该定义时不被其他代码影响。#define update_zmalloc_stat_add(__n) do { \ pthread_mutex_lock(&used_memory_mutex); \ used_memory += (__n); \ pthread_mutex_unlock(&used_memory_mutex); \} while(0)#define update_zmalloc_stat_sub(__n) do { \ pthread_mutex_lock(&used_memory_mutex); \ used_memory -= (__n); \ pthread_mutex_unlock(&used_memory_mutex); \} while(0)//更新分配计数#define update_zmalloc_stat_alloc(__n) do { \ size_t _n = (__n); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); /*保证分配的内存是sizeof(long)的整数倍*/\ if (zmalloc_thread_safe/*静态变量初始化为0*/) { \ update_zmalloc_stat_add(_n); \ } else { \ used_memory += _n; \ } \} while(0)//更新空闲计数#define update_zmalloc_stat_free(__n) do { \ size_t _n = (__n); \ if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \ if (zmalloc_thread_safe/*静态变量初始化为0*/) { \ update_zmalloc_stat_sub(_n); \ } else { \ used_memory -= _n; \ } \} while(0)static size_t used_memory = 0;static int zmalloc_thread_safe = 0;pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;static void zmalloc_default_oom(size_t size) { fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n", size); fflush(stderr); abort();}static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;//分配内存,大小为所需大小加前缀大小,然后返回给使用者可用的地址,也就是初始地址加上sizeof(size_t)的地址。void *zmalloc(size_t size) { void *ptr = malloc(size+PREFIX_SIZE/*sizeof(size_t) = */); if (!ptr) zmalloc_oom_handler(size); *((size_t*)ptr) = size; update_zmalloc_stat_alloc(size+PREFIX_SIZE); return (char*)ptr+PREFIX_SIZE;}void *zcalloc(size_t size) { void *ptr = calloc(1, size+PREFIX_SIZE); if (!ptr) zmalloc_oom_handler(size); *((size_t*)ptr) = size; update_zmalloc_stat_alloc(size+PREFIX_SIZE); return (char*)ptr+PREFIX_SIZE;}void *zrealloc(void *ptr, size_t size) { size_t oldsize; void *newptr; if (ptr == NULL) return zmalloc(size); realptr = (char*)ptr-PREFIX_SIZE; oldsize = *((size_t*)realptr); newptr = realloc(realptr,size+PREFIX_SIZE); if (!newptr) zmalloc_oom_handler(size); *((size_t*)newptr) = size; update_zmalloc_stat_free(oldsize); update_zmalloc_stat_alloc(size); return (char*)newptr+PREFIX_SIZE;}//释放内存,注意使用者传入的地址是加过sizeof(size_t)的void zfree(void *ptr) { void *realptr; size_t oldsize; if (ptr == NULL) return; realptr = (char*)ptr-PREFIX_SIZE; oldsize = *((size_t*)realptr); update_zmalloc_stat_free(oldsize+PREFIX_SIZE); free(realptr);}//将字符串存入按zmalloc规则分配的空间中char *zstrdup(const char *s) { size_t l = strlen(s)+1; char *p = zmalloc(l); memcpy(p,s,l); return p;}//返回已经使用的大小size_t zmalloc_used_memory(void) { size_t um; if (zmalloc_thread_safe) { pthread_mutex_lock(&used_memory_mutex); um = used_memory; pthread_mutex_unlock(&used_memory_mutex); } else { um = used_memory; } return um;}//设置多线程运行时线程安全的flagvoid zmalloc_enable_thread_safeness(void) { zmalloc_thread_safe = 1;}//重定向出错处理函数void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) { zmalloc_oom_handler = oom_handler;}#if defined(HAVE_PROC_STAT)#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>//在linux的proc目录下下打开pid所对应的文件并根据文件特点寻找第24个就是实际使用的物理内存(resident set size)size_t zmalloc_get_rss(void) { int page = sysconf(_SC_PAGESIZE); size_t rss; char buf[4096]; char filename[256]; int fd, count; char *p, *x; snprintf(filename,256,"/proc/%d/stat",getpid()); if ((fd = open(filename,O_RDONLY)) == -1) return 0; if (read(fd,buf,4096) <= 0) { close(fd); return 0; } close(fd); p = buf; count = 23; /* RSS is the 24th field in /proc/<pid>/stat */ while(p && count--) { p = strchr(p,' '); if (p) p++; } if (!p) return 0; x = strchr(p,' '); if (!x) return 0; *x = '\0'; rss = strtoll(p,NULL,10); rss *= page; return rss;}//获取碎片内存比率/* Fragmentation = RSS / allocated-bytes */float zmalloc_get_fragmentation_ratio(size_t rss) { return (float)rss/zmalloc_used_memory();}/* Get the sum of the specified field (converted form kb to bytes) in * /proc/self/smaps. The field must be specified with trailing ":" as it * apperas in the smaps output. * * Example: zmalloc_get_smap_bytes_by_field("Rss:"); */ //查询指定域的大小#if defined(HAVE_PROC_SMAPS)size_t zmalloc_get_smap_bytes_by_field(char *field) { char line[1024]; size_t bytes = 0; FILE *fp = fopen("/proc/self/smaps","r"); int flen = strlen(field); if (!fp) return 0; while(fgets(line,sizeof(line),fp) != NULL) { if (strncmp(line,field,flen) == 0) { char *p = strchr(line,'k'); if (p) { *p = '\0'; bytes += strtol(line+flen,NULL,10) * 1024; } } } fclose(fp); return bytes;}#endifsize_t zmalloc_get_private_dirty(void) { return zmalloc_get_smap_bytes_by_field("Private_Dirty:");}/* Returns the size of physical memory (RAM) in bytes. * It looks ugly, but this is the cleanest way to achive cross platform results. * Cleaned up from: * * http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system * * Note that this function: * 1) Was released under the following CC attribution license: * http://creativecommons.org/licenses/by/3.0/deed.en_US. * 2) Was originally implemented by David Robert Nadeau. * 3) Was modified for Redis by Matt Stancliff. * 4) This note exists in order to comply with the original license. */ //获取物理内存大小,在source insight中找不到打开的宏定义,删着就没了size_t zmalloc_get_memory_size(void) {#if defined(__unix__) || defined(__unix) || defined(unix) || \ (defined(__APPLE__) && defined(__MACH__))#else return 0L; /* Unknown OS. */#endif}
小技巧
以上就是zmalloc在linux下使用的所有代码,他分配内存的方式让我想起来了一个小技巧,使用一个结构体来管理数据:
struct TestMalloc{ long size; //记录大小 char data[]; //访问数据};
这样定义的话我们在分配内存时总是多分配sizeof(long),然后给size赋值,使用.data来访问可用内存存储数据,可以正确的输出结果。
#include <stdio.h>#include <stdlib.h>#include <string.h>int main(){ TestMalloc *ptr = (TestMalloc*)malloc(sizeof(long)+10); ptr->size = 10; memcpy(ptr->data,"test",sizeof("test")); printf("%s", ptr->data); free(ptr); getchar(); return 0;}
- 结合redis设计与实现的redis源码学习-1-内存分配(zmalloc)
- Redis源码分析(二十五)--- zmalloc内存分配实现
- Redis源码分析(二十五)--- zmalloc内存分配实现
- 【redis源码分析】内存分配---zmalloc
- 结合redis设计与实现的redis源码学习-8.1-object.c(对象实现)
- 结合redis设计与实现的redis源码学习-17-发布与订阅(pubsub.c)
- 结合redis设计与实现的redis源码学习-2-SDS(简单动态字符串)
- 结合redis设计与实现的redis源码学习-4-dict(字典)
- 结合redis设计与实现的redis源码学习-5-skiplist(跳跃表)
- 结合redis设计与实现的redis源码学习-6-intset(整数集合)
- 结合redis设计与实现的redis源码学习-7-ziplist(压缩列表)
- 结合redis设计与实现的redis源码学习-8.0-object(对象)
- 结合redis设计与实现的redis源码学习-10-hyperloglog(基数统计)
- 结合redis设计与实现的redis源码学习-8.2-t_string(字符串键)
- 结合redis设计与实现的redis源码学习-8.3-t_list.c(列表键)
- 结合redis设计与实现的redis源码学习-14-事件(ae.c/ae_epoll.c)
- 结合redis设计与实现的redis源码学习-15-TCP网络连接(anet.c)
- 结合redis设计与实现的redis源码学习-16-事务(multi.c)
- unity之KnapsackView
- virtualenv
- (一)linux系统移植过程和环境搭建
- sgu-264. Travel 稳定婚姻问题
- 树的算法_1,并查集
- 结合redis设计与实现的redis源码学习-1-内存分配(zmalloc)
- 给我放在中间
- LeetCode编程练习
- MQL5-真实型(双精度型,浮点型)
- 基于Android的指纹打卡功能的实现
- Gradle DSL method not found: 'compile()’
- C++模板(一)-函数模板
- maven项目导入eclipse之后代码无错,项目却报错,(修改maven默认的jdk版本)
- 省SD2017 I Parity check【大数+规律】