结合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;}
阅读全文
0 0
原创粉丝点击