zmalloc.{c,h}是对Redis内存进行一系列管理的文件,包括对malloc的封装,以及一些内存的状态数据分析。
这里面有一个比较重要的变量叫used_memory,他记录了Redis一共分配了多少的内存,后面的大部分函数都是跟这个变量打交道。
这些函数都是封装了C函数库中的malloc calloc realloc free,主要是为了记录Redis分配的内存大小,虽然malloc本身会在首地址前面记录分配的大小(不然free怎么知道要释放多少内存),但是这个大小程序员并不能直接获取,所以需要封装记录。
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
}
在分配内存时多分配PREFIX_SIZE大小用于记录size的大小,update_zmalloc_stat_alloc是用来更新Redis一共分配了多少内存,最后返回地址需要加PREFIX_SIZE来返回给上层,因为PREFIX_SIZE这部分对上层是透明的。zcalloc zrealloc这两个函数与zmalloc类似。
size_t zmalloc_size(void *ptr) {
void *realptr = (char*)ptr-PREFIX_SIZE;
size_t size = *((size_t*)realptr);
/* Assume at least that all the allocations are padded at sizeof(long) by
* the underlying allocator. */
if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
return size+PREFIX_SIZE;
}
zmalloc_size用于获取内存的大小,只需要将首地址减去PREFIX_SIZE就可以读取到分配时存入的分配内存大小了。
void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif
if (ptr == NULL) return;
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_free(zmalloc_size(ptr));
free(ptr);
#else
realptr = (char*)ptr-PREFIX_SIZE;
oldsize = *((size_t*)realptr);
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
free(realptr);
#endif
}
释放空间记得吧前面PREFIX_SIZE部分也要释放,然后用update_zmalloc_stat_free更新used_memory。
RSS指实际内存占用大小
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//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;
}
这个函数是通过打开系统文件然后进行一系列的处理获取这个进程(Redis)的RSS。
float zmalloc_get_fragmentation_ratio(size_t rss) {
return (float)rss/zmalloc_used_memory();
}
通过rss和used_memory可以计算碎片率。
size_t zmalloc_get_private_dirty(void) {
char line[1024];
size_t pd = 0;
FILE *fp = fopen("/proc/self/smaps","r");
if (!fp) return 0;
while(fgets(line,sizeof(line),fp) != NULL) {
if (strncmp(line,"Private_Dirty:",14) == 0) {
char *p = strchr(line,'k');
if (p) {
*p = '\0';
pd += strtol(line+14,NULL,10) * 1024;
}
}
}
fclose(fp);
return pd;
}
获取Private_Dirty的值,这个值我的理解是被多个进程共享的内存。
还有一些oom的函数和stl中类似就不写了。这个文件还算简单,接下来我会看Redis的5种数据类型。