当我第一次阅读了这个文件的源码的时候,我笑了,忽然想起前几周阿里电话二面的时候,问到了自定义内存管理函数并处理8字节对齐问题。当时无言以对,在面试官无数次的提示下才答了出来,结果显而易见,挂掉了二面。而这份源码中函数zmalloc()和zfree()的设计思路和实现原理,正是面试官想要的答案。
源码结构
zmalloc.c文件的内容如下:
主要函数
- zmalloc()
- zfree()
- zcalloc()
- zrelloc()
- zstrdup()
字长与字节对齐
CPU一次性能读取数据的二进制位数称为
字长,也就是我们通常所说的32位系统(字长4个字节)、64位系统(字长8个字节)的由来。所谓的8字节对齐,就是指变量的起始地址是8的倍数。比如程序运行时(CPU)在读取long型数据的时候,只需要一个总线周期,时间更短,如果不是8字节对齐的则需要两个总线周期才能读完数据。
本文中我提到的8字节对齐是针对64位系统而言的,如果是32位系统那么就是4字节对齐。实际上Redis源码中的字节对齐是软编码,而非硬编码。里面多用sizeof(long)或sizeof(size_t)来表示。size_t(gcc中其值为long unsigned int)和long的长度是一样的,long的长度就是计算机的字长。这样在未来的系统中如果字长(long的大小)不是8个字节了,该段代码依然能保证相应代码可用。
zmalloc
辅助的函数:
- malloc()
- zmalloc_oom_handler【函数指针】
- zmalloc_default_oom()【被上面的函数指针所指向】
- update_zmalloc_stat_alloc()【宏函数】
- update_zmalloc_stat_add()【宏函数】
zmalloc()和malloc()有相同的函数接口(参数,返回值)。
zmalloc()源码
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
}
参数size是我们
需要分配的内存大小。实际上我们调用malloc
实际分配的大小是size+PREFIX_SIZE。PREFIX_SIZE是一个条件编译的宏,不同的平台有不同的结果,在Linux中其值是sizeof(size_t),所以我们多分配了一个字长(8个字节)的空间(后面代码可以看到多分配8个字节的目的是用于储存size的值)。
如果ptr指针为NULL(内存分配失败),调用zmalloc_oom_handler(size)。该函数实际上是一个函数指针指向函数zmalloc_default_oom,其主要功能就是打印错误信息并终止程序。
// oom是out of memory(内存不足)的意思
static void zmall