前言
zmalloc.c
和zmalloc.h
主要功能就是对原有库里的内存分配函数进行封装,形成独立的一套内存管理函数。由于redis
要求满足跨平台性,而每个平台又会有自己的内存管理函数,所以在这两个文件中,将会看到大量的#ifdef
,根据系统的不同,使用不同的内存管理函数,而封装接口都是一致的--zmalloc
。下面将会对主要的几个函数进行源码分析。
update_zmalloc_stat_alloc
这个宏定义的功能就是计算当前使用的内存的大小。atomicIncr
是一个原子操作,执行的是一个加法运算,used_memory
是一个static
变量,其数值代表所用的内存大小。在这之前对_n
的修改是为了进行字节对齐。宏update_zmalloc_stat_free
则执行内存减少的记录。
#define update_zmalloc_stat_alloc(__n) do { \
size_t _n = (__n); \
if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
atomicIncr(used_memory,__n); \
} while(0)
zmalloc
zmalloc
、zcalloc
和zrealloc
内部的对原有内存管理函数的处理都是一样的,所以这里只讨论zmalloc
。
void *zmalloc(size_t size) {
void *ptr = malloc(size+PREFIX_SIZE); //#1
if (!ptr) zmalloc_oom_handler(size); //#2
#ifdef HAVE_MALLOC_SIZE //#3
update_zmalloc_stat_alloc(zmalloc_size(ptr)); //#4
return ptr;
#else //#5
*((size_t*)ptr) = size; //#6
update_zmalloc_stat_alloc(size+PREFIX_SIZE); //#7
return (char*)ptr+PREFIX_SIZE; //#8
#endif
}
#1
:当需要分配内存时,需要多分配PREFIX_SIZE
个字节用于记录本次内存分配的大小。有的系统内存管理函数自带计算分配字节的函数,所以PREFIX_SIZE
有时是0。#2
:zmalloc_oom_handler
为错误处理函数。#3
和#4
:这部分就是因为有的系统自带计算内存大小的函数,所以PREFIX_SIZE
为0,直接返回指针就行。#5
和#6
:当需要手动计算内存大小时,就在指针首端存放内存大小信息。#7
:修改used_memory
。#8
:需要返回实际分配的内存开始地址,所以需要加上PREFIX_SIZE
。
zfree
内存释放函数,和zmalloc
函数操作相反。为了直观的说明程序,只展示主要部分。
void zfree(void *ptr) {
...
realptr = (char*)ptr-PREFIX_SIZE; //#1
oldsize = *((size_t*)realptr);
update_zmalloc_stat_free(oldsize+PREFIX_SIZE);
free(realptr);
...
}
#1
: 需要注意的是ptr
指针不能直接free
,因为实际分配内存的大小需要减去PREFIX_SIZE
,所以应该free
减去过后的指针。
zmalloc_get_rss
该函数的功能返回该进程实际消耗的物理内存的大小,这也是rss
的意思。为了直观的说明程序,只展示主要部分。
size_t zmalloc_get_rss(void) {
int page = sysconf(_SC_PAGESIZE); //#1
...
snprintf(filename,256,"/proc/%d/stat",getpid()); //#2
if ((fd = open(filename,O_RDONLY)) == -1) return 0;
if (read(fd,buf,4096) <= 0) {
...
}
close(fd);
p = buf; //#3
count = 23; /* RSS is the 24th field in /proc/<pid>/stat */
while(p && count--) { //#4
p = strchr(p,' ');
if (p) p++;
}
x = strchr(p,' ');
*x = '\0'; //#5
rss = strtoll(p,NULL,10); //#6
rss *= page; //#7
return rss;
}
#1
: 这是一个系统调用,系统中的内存是以页为单位,该系统调用返回一页的字节大小。#2
:/proc/<pid号>/stat
文件的第24行存放着进程的RSS。#3
:从文件中读取所有的内容。#4~#6
:主要就是找到第24行的内容,然后读取内容该行内容转化成整型。为了清除理解为啥要想这样计算,我随便打开了一个进程的stat
文件,显示如下内容: 可见每一行之间其实只有一个' '
字符相隔。所以只要找到第24个字符串,然后在结尾加上\0
,通过strtoll
函数将这个字符串转化成整型即可。#7
:页数乘上每页的大小即可获得物理内存消耗大小。