Redis源码阅读(一)

写在前面:因为目前的工作需要长期跟redis打交道,然后自己也想把这块吃透,方便技术提升,所以写博客。

Redis源码阅读(一)

第一阶段阅读Redis的数据结构部分,基本位于如下文件中:
内存分配 zmalloc.c和zmalloc.h
动态字符串 sds.h和sds.c
双端链表 adlist.c和adlist.h
字典 dict.h和dict.c
跳跃表 server.h文件
里面关于zskiplist结构和zskiplistNode结构,以及t_zset.c中所有zsl开头的函数,比如 zslCreate、zslInsert、zslDeleteNode等等
基数统计 hyperloglog.c
其中的 hllhdr 结构,以及所有以 hll 开头的函数

zmalloc.h

Redis在这个版本使用三种选择作为allocator,所以先介绍部分宏定义

allocator

a) tcmalloc:由google用于优化C++多线程应用而开发。Redis 需要1.6以上的版本。

b) jemalloc:第一次用在FreeBSD 的allocator,于2005年释出的版本。强调降低碎片化,可扩展的并行支持。Redis需要2.1以上版本。

c) libc:最常使用的libc库。GNU libc,默认使用此allocator。

#if defined(USE_TCMALLOC)
#define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
#include <google/tcmalloc.h>
#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) tc_malloc_size(p)
#else
#error "Newer version of tcmalloc required"
#endif

#elif defined(USE_JEMALLOC)
#define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
#include <jemalloc/jemalloc.h>
#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) je_malloc_usable_size(p)
#else
#error "Newer version of jemalloc required"
#endif

#elif defined(__APPLE__)
#include <malloc/malloc.h>
#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_size(p)
#endif

/* On native libc implementations, we should still do our best to provide a
 * HAVE_MALLOC_SIZE capability. This can be set explicitly as well:
 *
 * NO_MALLOC_USABLE_SIZE disables it on all platforms, even if they are
 *      known to support it.
 * USE_MALLOC_USABLE_SIZE forces use of malloc_usable_size() regardless
 *      of platform.
 */
#ifndef ZMALLOC_LIB
#define ZMALLOC_LIB "libc"

#if !defined(NO_MALLOC_USABLE_SIZE) && \
    (defined(__GLIBC__) || defined(__FreeBSD__) || \
     defined(USE_MALLOC_USABLE_SIZE))

/* Includes for malloc_usable_size() */
#ifdef __FreeBSD__
#include <malloc_np.h>
#else
#include <malloc.h>
#endif

#define HAVE_MALLOC_SIZE 1
#define zmalloc_size(p) malloc_usable_size(p)

#endif
#endif

由宏USE_TCMALLOC,USE_JEMALLOC和__APPLE__控制要编译进Redis的allocator。 前两个宏从make 传入,后面一个是操作系统宏,若是Apple,则可以提供一个 malloc_size (),用于查看指针指向内存的大小。此函数在jemalloc和tcmalloc中都有提供,但glibc中不提供此函数,宏HAVE_MALLOC_SIZE即是用于控制此函数。

src/zmalloc.c

zmalloc.c 读完后感觉是解决字长和字节对齐的问题,方便跨平台

void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
size_t zmalloc_used_memory(void);
void zmalloc_enable_thread_safeness(void);
float zmalloc_get_fragmentation_ratio(size_t rss);
size_t zmalloc_get_rss(void);

PREFIX_SIZE

根据机器的不同,定义为一个字长大小

#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0)
#else
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
#define PREFIX_SIZE (sizeof(long long))
#else
#define PREFIX_SIZE (sizeof(size_t))
#endif
#endif

update_zmalloc_stat_alloc和atomicIncr

update_zmalloc_stat_alloc,因为sizeof(long) == 8 [64位系统中],所以其实第一个if的代码等价于if(_n&7) _n += 8 - (_n&7); 这段代码就是判断分配的内存空间的大小是不是8的倍数。如果内存大小不是8的倍数,就加上相应的偏移量使之变成8的倍数。
这是为了精确计算系统分配的空间,因为malloc会自动做内存对齐,分配空间可能会比实际需要数值略多一点。

#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); \      //注4:atomicIncr
} while(0)

atomicIncr是Increment the atomic counter 增加原子计数器

#define atomicIncr(var,count) do { \
    pthread_mutex_lock(&var ## _mutex); \   //线程安全
    var += (count); \
    pthread_mutex_unlock(&var ## _mutex); \
} while(0)

ZMALLOC

/* Try allocating memory, and return NULL if failed.
 * '*usable' is set to the usable size if non NULL. */

void *ztrymalloc_usable(size_t size, size_t *usable) {
    /* Possible overflow, return NULL, so that the caller can panic or handle a failed allocation. */
    if (size >= SIZE_MAX/2) return NULL;//大于可用空间的一半直接返回空
    void *ptr = malloc(MALLOC_MIN_SIZE(size)+PREFIX_SIZE);

    if (!ptr) return NULL;
#ifdef HAVE_MALLOC_SIZE
    size = zmalloc_size(ptr); //zmalloc_size()是为了给系统本身没有malloc函数的分配空间,第一个字节存储信息,信息存储在prefix_size里面
    update_zmalloc_stat_alloc(size);//对齐8字节
    if (usable) *usable = size;
    return ptr;
#else
    *((size_t*)ptr) = size;
    update_zmalloc_stat_alloc(size+PREFIX_SIZE);
    if (usable) *usable = size;
    return (char*)ptr+PREFIX_SIZE;
#endif
}

/* Allocate memory or panic */
void *zmalloc(size_t size) {
    void *ptr = ztrymalloc_usable(size, NULL); //可用空间的申请
    if (!ptr) zmalloc_oom_handler(size); /*out of memory的处理*/
    return ptr;
}

zcalloc

同理malloc,为没有自带calloc的平台提供分配空间并赋值为0的操作

/* Allocate memory and zero it or panic */
void *zcalloc(size_t size) {
    void *ptr = ztrycalloc_usable(size, NULL);
    if (!ptr) zmalloc_oom_handler(size);
    return ptr;
}


/* Try allocating memory and zero it, and return NULL if failed.
 * '*usable' is set to the usable size if non NULL. */
void *ztrycalloc_usable(size_t size, size_t *usable) {
    /* Possible overflow, return NULL, so that the caller can panic or handle a failed allocation. */
    if (size >= SIZE_MAX/2) return NULL;
    void *ptr = calloc(1, MALLOC_MIN_SIZE(size)+PREFIX_SIZE);
    if (ptr == NULL) return NULL;

#ifdef HAVE_MALLOC_SIZE
    size = zmalloc_size(ptr);
    update_zmalloc_stat_alloc(size);
    if (usable) *usable = size;
    return ptr;
#else
    *((size_t*)ptr) = size;
    update_zmalloc_stat_alloc(size+PREFIX_SIZE);
    if (usable) *usable = size;
    return (char*)ptr+PREFIX_SIZE;
#endif
}

ZREALLOC

zrealloc函数是修改所指向空间的大小。这里首先获取原空间的大小oldsize,再分配尺寸为size的新空间。如果成功,则老空间所指内容会被复制进新空间,且老空间指针失效,新空间指针为newptr。函数会根据HAVE_MALLOC_SIZE宏的定义确定是否需要额外分配PREFIX_SIZE大小设置空间的长度。

首先了解下

update_zmalloc_stat_free

update_zmalloc_stat_free会首先根据long型对齐占用的空间大小,然后对used_memery减去计算好的尺寸

#define update_zmalloc_stat_free(__n) do { \
    size_t _n = (__n); \
    if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); \
    atomicDecr(used_memory,__n); \         
} while(0)

/*原子操作 减*/
#define atomicDecr(var,count) do { \
    pthread_mutex_lock(&var ## _mutex); \
    var -= (count); \
    pthread_mutex_unlock(&var ## _mutex); \
} while(0)

然后再看ZREALLOC

/* Try allocating memory, and return NULL if failed. */
void *ztrycalloc(size_t size) {
    void *ptr = ztrycalloc_usable(size, NULL);
    return ptr;
}

/* Try reallocating memory, and return NULL if failed.
 * '*usable' is set to the usable size if non NULL. */
void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable) {
#ifndef HAVE_MALLOC_SIZE
    void *realptr;
#endif
    size_t oldsize;
    void *newptr;

		//size为0 直接释放后返回
    /* not allocating anything, just redirect to free. */
    if (size == 0 && ptr != NULL) {
        zfree(ptr);
        if (usable) *usable = 0;
        return NULL;
    }
    //原空间为0 直接分配
    /* Not freeing anything, just redirect to malloc. */
    if (ptr == NULL)
        return ztrymalloc_usable(size, usable);

    /* Possible overflow, return NULL, so that the caller can panic or handle a failed allocation. */
    //可能溢出,所以返回为空
    if (size >= SIZE_MAX/2) {
        zfree(ptr);
        if (usable) *usable = 0;
        return NULL;
    }

#ifdef HAVE_MALLOC_SIZE
		//本身有realloc函数,update_zmalloc_stat_free减去旧的空间,再加上申请的新空间
    oldsize = zmalloc_size(ptr);
    newptr = realloc(ptr,size);
    if (newptr == NULL) {
        if (usable) *usable = 0;
        return NULL;
    }
		//先释放旧的空间
    update_zmalloc_stat_free(oldsize);
    size = zmalloc_size(newptr);
    //再申请新空间
    update_zmalloc_stat_alloc(size);
    if (usable) *usable = size;
    return newptr;
//本身没有realloc函数,update_zmalloc_stat_free减去旧的空间
#else
		//减去prefix
    realptr = (char*)ptr-PREFIX_SIZE;
    oldsize = *((size_t*)realptr);
    //重新申请空间
    newptr = realloc(realptr,size+PREFIX_SIZE);
    if (newptr == NULL) {
        if (usable) *usable = 0;
        return NULL;
    }

    *((size_t*)newptr) = size;
    //释放
    update_zmalloc_stat_free(oldsize);
    //申请
    update_zmalloc_stat_alloc(size);
    if (usable) *usable = size;
    return (char*)newptr+PREFIX_SIZE;
#endif
}
}


zstrdup

zstrdup函数是把一份空间的内容,分配并拷贝内容至新空间,并返回新空间的指针。

char *zstrdup(const char *s) {
    size_t l = strlen(s)+1;
    char *p = zmalloc(l);

    memcpy(p,s,l);
    return p;
}

zmalloc_used_memory

zmalloc_used_memory 函数用来获取当前使用的内存总量,其中__sync_add_and_fetch就是宏update_zmalloc_stat_add。

size_t zmalloc_used_memory(void) {
    size_t um;
    atomicGet(used_memory,um);
    return um;
}

#define atomicGet(var,dstvar) do { \
    pthread_mutex_lock(&var ## _mutex); \
    dstvar = var; \
    pthread_mutex_unlock(&var ## _mutex); \
} while(0)

zmalloc_get_rss

这个函数可以获取当前进程实际所驻留在内存中的空间大小,即不包括被交换(swap)出去的空间。该函数大致的操作就是在当前进程的 /proc//stat 【表示当前进程id】文件中进行检索。该文件的第24个字段是RSS的信息,它的单位是pages(内存页的数目)。如果没从操作系统的层面获取驻留内存大小,那就只能绌劣的返回已经分配出去的内存大小。

根据不同的宏定义,win10下面高亮的源码已展示,就是

#if defined(HAVE_PROC_STAT)
#elif defined(HAVE_TASKINFO)
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#elif defined(__NetBSD__) || defined(__OpenBSD__)
#elif defined(__HAIKU__)
#elif defined(HAVE_PSINFO)
#else
size_t zmalloc_get_rss(void) {
    /* If we can't get the RSS in an OS-specific way for this system just
     * return the memory usage we estimated in zmalloc()..
     *
     * Fragmentation will appear to be always 1 (no fragmentation)
     * of course... */
	//如果我们不能以特定于操作系统的方式为这个系统获取RSS,那么就返回我们在zmalloc()中估计的内存使用量。
    //碎片总是1(没有碎片)
    return zmalloc_used_memory();
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值