zoukankan      html  css  js  c++  java
  • Redis源码系列(一)

    Redis源码系列——内存管理

    函数原型 src/zmalloc.h

    函数指针与void*指针的使用,提供了一个泛型的机制

    /*stringfication*/
    #define __xstr(s) __str(s)
    #define __str(s) #s
    /*prototypes*/
    void *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);
    #ifndef HAVE_MALLOC_SIZE
    size_t zmalloc_size(void *ptr);
    #endif
    
    

    函数实现src/zmalloc.c

    几个全局静态量

    /*已经使用的内存*/
    static size_t used_memory = 0;
    /*线程安全标志 全局静态变量*/
    static int zmalloc_thread_safe = 0;
    /*锁*/
    pthread_mutex_t used_memory_mutex = PTHREAD_MUTEX_INITIALIZER;
    

    1.zlic_free

    提供原始的libc内存free函数,在包含zmalloc.h之前定义.

    /* This function provide us access to the original libc free(). This is useful
     * for instance to free results obtained by backtrace_symbols(). We need
     * to define this function before including zmalloc.h that may shadow the
     * free implementation if we use jemalloc or another non standard allocator. */
    void zlibc_free(void *ptr) {
        free(ptr);
    }
    

    2.PREFIX_SIZE

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

    #if defined(__sun) || defined(__sparc) || defined(__sparc__)
    #define PREFIX_SIZE (sizeof(long long))
    #else
    #define PREFIX_SIZE (sizeof(size_t))
    

    3.zmalloc

    redis的内存申请函数,内部用malloc函数实现.

    /*
     * zmalloc , zcalloc ,zrealloc
     * 都申请了多一个PREFIX_SZIE 的内存大小,并与字长对齐
     */
    void *zmalloc(size_t size) {
        /*size 为实际需要的大小
         * PREFIX_SIZE 为预编译宏:根据机器而定,用于存储size的值*/
        void *ptr = malloc(size+PREFIX_SIZE);
        /*错误处理:调用函数default_oom*/
        if (!ptr) zmalloc_oom_handler(size);
    #ifdef HAVE_MALLOC_SIZE
        update_zmalloc_stat_alloc(zmalloc_size(ptr));
        return ptr;
    #else
        /*分配内存的第一个字长放上 size的值*/
        *((size_t*)ptr) = size;
        /*更新已经使用的内存大小全局量*/
        update_zmalloc_stat_alloc(size+PREFIX_SIZE);
        /*向右偏移PREFIX_SIZE 此时指针指向的空间的大小就是size*/
        //     +--------------+-----------------+
        //     | PREFIX_SIZE  | size            |
        //     +--------------+-----------------+
        //     ^              ^
        //     |              |
        //    ptr            (char*)ptr+PREFIX_SIZE
        // 也是返回的指针指向的地址
        return (char*)ptr+PREFIX_SIZE;
    #endif
    }
    

    关于错误处理的函数,zmalloc_oom_handler实际为一函数指针

    /*错误处理,并退出*/
    static void zmalloc_default_oom(size_t size) {
        fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes
    ",
            size);
        fflush(stderr);
        abort();
    }
    /*函数指针*/
    static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
    
    

    其值,指向默认的oom(out of memory)处理函数.

    至于维护内存大小全局变量的update_zmalloc_stat_alloc则为一个宏函数,其实现如下:

    /*使得变量used_memory精确的维护实际分配的内存*/
    #define update_zmalloc_stat_alloc(__n) do { 
        /*转为size_t*/
        size_t _n = (__n); 
        /* 
         * 判断是否与字长对齐,对于64位机器,内存是否与8对齐
         * 不对齐就加上一定的偏移量使之对齐
         */
        if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); 
        /*是否需要保证线程安全*/
        if (zmalloc_thread_safe) { 
            update_zmalloc_stat_add(_n); 
        } else { 
            used_memory += _n; 
        } 
    } while(0)
    

    这里有几个地方值得注意:

    1. 宏中使用的do{...}while(0)技巧

    2. 判断是否对其的位运算操作, 与取模运算一致

      [a \%(2^n)=a&(2^n-1) ]

    3. 是否需要线程安全的实现方式,如果需要就调用update_zmalloc_stat_add,不然就直接增加used_memory

    线程安全方法中用到的宏,实现如下:

    #ifdef HAVE_ATOMIC
    #define update_zmalloc_stat_add(__n) __sync_add_and_fetch(&used_memory, (__n))
    #else
    /* 
     * 线程安全方法更新,使用互斥锁(mutex)保证线程安全
     * 由update_zmalloc_stat_alloc调用
     * 先加锁,然后更新最后再解锁
     */
    #define update_zmalloc_stat_add(__n) do { 
        pthread_mutex_lock(&used_memory_mutex); 
        /*线程安全*/
        used_memory += (__n); 
        pthread_mutex_unlock(&used_memory_mutex); 
    } while(0)
    
    #endif
    

    此处用到的互斥锁等,均源自POSIX多线程,即pthread.h

    既然在allocation的时候有这样一套机制,那么在free的时候会有对应的宏来维护内存大小量.

    #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_free(__n) do { 
        size_t _n = (__n); 
        if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); 
        if (zmalloc_thread_safe) { 
            update_zmalloc_stat_sub(_n); 
        } else { 
            used_memory -= _n; 
        } 
    } while(0)
    
    

    对应着看,应该很好理解.

    4.zcalloc

    内部调用calloc实现

    void *zcalloc(size_t size) {
        /*
         * calloc是线程安全函数
         * 分配的内存大小为 num*size
         * 并初始化为0
         */
        void *ptr = calloc(1, 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
    }
    

    5.zrealloc

    在此之前,先看看realloc函数

    重新(扩大)分配内存函数,内部调用realloc函数

    /*重新分配内存*/
    void *zrealloc(void *ptr, size_t size) {
    #ifndef HAVE_MALLOC_SIZE
        void *realptr;
    #endif
        size_t oldsize;
        void *newptr;
    
        /*重新申请一块内存并返回*/
        if (ptr == NULL) return zmalloc(size);
    #ifdef HAVE_MALLOC_SIZE
        oldsize = zmalloc_size(ptr);
    	/*calloc重新申请内存*/
        newptr = realloc(ptr,size);
        if (!newptr) zmalloc_oom_handler(size);
    	/*free原来的内存*/
        update_zmalloc_stat_free(oldsize);
    	/*更新全局量 used_memory*/
        update_zmalloc_stat_alloc(zmalloc_size(newptr));
        return newptr;
    #else
     	/*向前PREFIX_SIZE*/
        realptr = (char*)ptr-PREFIX_SIZE;
    	/*原来内存的大小*/
        oldsize = *((size_t*)realptr);
    	/*重新申请内存*/
        newptr = realloc(realptr,size+PREFIX_SIZE);
        if (!newptr) zmalloc_oom_handler(size);
    	/*存储size*/
        *((size_t*)newptr) = size;
    	/*free原来的空间*/
        update_zmalloc_stat_free(oldsize);
    	/*更新全局量 used_memory*/
        update_zmalloc_stat_alloc(size);
        return (char*)newptr+PREFIX_SIZE;
    #endif
    }
    

    其余部分的实现都很好理解.

    6.zmalloc_size

    /* Provide zmalloc_size() for systems where this function is not provided by
     * malloc itself, given that in that case we store a header with this
     * information as the first bytes of every allocation.
     *
     */
    #ifndef HAVE_MALLOC_SIZE
    size_t zmalloc_size(void *ptr) {
        /* malloc的内存的大小*/
        /*向前偏移一个字长*/
        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;
    }
    #endif
    

    7.zstrdup

    复制字符串函数

    char *zstrdup(const char *s) {
        /*多出来一个*/
        size_t l = strlen(s)+1;
        char *p = zmalloc(l);
        memcpy(p,s,l);
        return p;
    }
    

    8. zmalloc_used_memory

    实现了线程安全方法

    /*返回used_memory 线程安全*/
    size_t zmalloc_used_memory(void) {
        size_t um;
    
        if (zmalloc_thread_safe) {
    #ifdef HAVE_ATOMIC
            um = __sync_add_and_fetch(&used_memory, 0);
    #else
            /*加锁*/
            pthread_mutex_lock(&used_memory_mutex);
            um = used_memory;
            /*解锁*/
            pthread_mutex_unlock(&used_memory_mutex);
    #endif
        }/*保证线程安全*/
        else {
            um = used_memory;
        }
        return um;
    }
    

    9. help functions

    /*
     * 是否需要线程安全
     * 0代表不需要 
     */
    void zmalloc_enable_thread_safeness(void) {
        zmalloc_thread_safe = 1;
    }
    /* oom状态下采取的操作: out of memory
     * 默认为 zmalloc_default_oom()
     */
    void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
        zmalloc_oom_handler = oom_handler;
    }
    

    10.zmalloc_get_rss

    返回当前进程实际驻留在内存中的大小,与操作系统相关

    #if defined(HAVE_PROC_STAT)
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    size_t zmalloc_get_rss(void) {
        /*sysconf为系统函数,获取页大小*/
        int page = sysconf(_SC_PAGESIZE);
        size_t rss;
        char buf[4096];
        char filename[256];
        int fd, count;
        char *p, *x;
    
    	/*将当前进程所对应的stat文件的绝对路径保存到filename*/
        snprintf(filename,256,"/proc/%d/stat",getpid());
    	/*只读打开stat文件*/
        if ((fd = open(filename,O_RDONLY)) == -1) return 0;
        if (read(fd,buf,4096) <= 0) {
            close(fd);
            return 0;
        }
        close(fd);
    	/*读完信息,存到buf*/
        p = buf;
        count = 23; /* RSS 是 /proc/<pid>/stat 的第24个字段*/
        while(p && count--) {
    		/*查找空格,由空格分隔字段*/
            p = strchr(p,' ');
    		/*指向下一个字段首地址*/
            if (p) p++;
        }
        if (!p) return 0;
        x = strchr(p,' ');
        if (!x) return 0;
        *x = '';
    	/*string to long long*/
        rss = strtoll(p,NULL,10);
    	/*rss获取的是内存页的页数,乘以页大小即可知*/
        rss *= page;
        return rss;
    }
    #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... 
         *
         * 如果不能通过操作系统来获得,就直接返回used_memory.
         */
        return zmalloc_used_memory();
    }
    #endif
    
    /* 
     * Fragmentation = RSS / allocated-bytes 
     * 内存碎片率
     */
    float zmalloc_get_fragmentation_ratio(size_t rss) {
        return (float)rss/zmalloc_used_memory();
    }
    
  • 相关阅读:
    ActiveSync合作关系对话框的配置
    WINCE对象存储区(object store)
    Wince 隐藏TASKBAR的方法
    Wince输入法换肤换语言机制
    poj 3080 Blue Jeans 解题报告
    codeforces A. Vasily the Bear and Triangle 解题报告
    hdu 1050 Moving Tables 解题报告
    hdu 1113 Word Amalgamation 解题报告
    codeforces A. IQ Test 解题报告
    poj 1007 DNA Sorting 解题报告
  • 原文地址:https://www.cnblogs.com/oasisyang/p/14296730.html
Copyright © 2011-2022 走看看