经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
Redis源码系列(一)
来源:cnblogs  作者:OasisYang  时间:2021/1/25 11:06:22  对本文有异议

Redis源码系列——内存管理

函数原型 src/zmalloc.h

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

  1. /*stringfication*/
  2. #define __xstr(s) __str(s)
  3. #define __str(s) #s
  4. /*prototypes*/
  5. void *zmalloc(size_t size);
  6. void *zcalloc(size_t size);
  7. void *zrealloc(void *ptr, size_t size);
  8. void zfree(void *ptr);
  9. char *zstrdup(const char *s);
  10. size_t zmalloc_used_memory(void);
  11. void zmalloc_enable_thread_safeness(void);
  12. void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
  13. float zmalloc_get_fragmentation_ratio(size_t rss);
  14. size_t zmalloc_get_rss(void);
  15. #ifndef HAVE_MALLOC_SIZE
  16. size_t zmalloc_size(void *ptr);
  17. #endif

函数实现src/zmalloc.c

几个全局静态量

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

1.zlic_free

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

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

2.PREFIX_SIZE

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

  1. #if defined(__sun) || defined(__sparc) || defined(__sparc__)
  2. #define PREFIX_SIZE (sizeof(long long))
  3. #else
  4. #define PREFIX_SIZE (sizeof(size_t))

3.zmalloc

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

  1. /*
  2. * zmalloc , zcalloc ,zrealloc
  3. * 都申请了多一个PREFIX_SZIE 的内存大小,并与字长对齐
  4. */
  5. void *zmalloc(size_t size) {
  6. /*size 为实际需要的大小
  7. * PREFIX_SIZE 为预编译宏:根据机器而定,用于存储size的值*/
  8. void *ptr = malloc(size+PREFIX_SIZE);
  9. /*错误处理:调用函数default_oom*/
  10. if (!ptr) zmalloc_oom_handler(size);
  11. #ifdef HAVE_MALLOC_SIZE
  12. update_zmalloc_stat_alloc(zmalloc_size(ptr));
  13. return ptr;
  14. #else
  15. /*分配内存的第一个字长放上 size的值*/
  16. *((size_t*)ptr) = size;
  17. /*更新已经使用的内存大小全局量*/
  18. update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  19. /*向右偏移PREFIX_SIZE 此时指针指向的空间的大小就是size*/
  20. // +--------------+-----------------+
  21. // | PREFIX_SIZE | size |
  22. // +--------------+-----------------+
  23. // ^ ^
  24. // | |
  25. // ptr (char*)ptr+PREFIX_SIZE
  26. // 也是返回的指针指向的地址
  27. return (char*)ptr+PREFIX_SIZE;
  28. #endif
  29. }

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

  1. /*错误处理,并退出*/
  2. static void zmalloc_default_oom(size_t size) {
  3. fprintf(stderr, "zmalloc: Out of memory trying to allocate %zu bytes\n",
  4. size);
  5. fflush(stderr);
  6. abort();
  7. }
  8. /*函数指针*/
  9. static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;

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

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

  1. /*使得变量used_memory精确的维护实际分配的内存*/
  2. #define update_zmalloc_stat_alloc(__n) do { /*转为size_t*/
  3. size_t _n = (__n); /*
  4. * 判断是否与字长对齐,对于64位机器,内存是否与8对齐
  5. * 不对齐就加上一定的偏移量使之对齐
  6. */
  7. if (_n&(sizeof(long)-1)) _n += sizeof(long)-(_n&(sizeof(long)-1)); /*是否需要保证线程安全*/
  8. 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

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

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

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

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

  1. #define update_zmalloc_stat_sub(__n) do { pthread_mutex_lock(&used_memory_mutex); used_memory -= (__n); pthread_mutex_unlock(&used_memory_mutex); } while(0)
  2. #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实现

  1. void *zcalloc(size_t size) {
  2. /*
  3. * calloc是线程安全函数
  4. * 分配的内存大小为 num*size
  5. * 并初始化为0
  6. */
  7. void *ptr = calloc(1, size+PREFIX_SIZE);
  8. if (!ptr) zmalloc_oom_handler(size);
  9. #ifdef HAVE_MALLOC_SIZE
  10. update_zmalloc_stat_alloc(zmalloc_size(ptr));
  11. return ptr;
  12. #else
  13. *((size_t*)ptr) = size;
  14. update_zmalloc_stat_alloc(size+PREFIX_SIZE);
  15. return (char*)ptr+PREFIX_SIZE;
  16. #endif
  17. }

5.zrealloc

在此之前,先看看realloc函数

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

  1. /*重新分配内存*/
  2. void *zrealloc(void *ptr, size_t size) {
  3. #ifndef HAVE_MALLOC_SIZE
  4. void *realptr;
  5. #endif
  6. size_t oldsize;
  7. void *newptr;
  8. /*重新申请一块内存并返回*/
  9. if (ptr == NULL) return zmalloc(size);
  10. #ifdef HAVE_MALLOC_SIZE
  11. oldsize = zmalloc_size(ptr);
  12. /*calloc重新申请内存*/
  13. newptr = realloc(ptr,size);
  14. if (!newptr) zmalloc_oom_handler(size);
  15. /*free原来的内存*/
  16. update_zmalloc_stat_free(oldsize);
  17. /*更新全局量 used_memory*/
  18. update_zmalloc_stat_alloc(zmalloc_size(newptr));
  19. return newptr;
  20. #else
  21. /*向前PREFIX_SIZE*/
  22. realptr = (char*)ptr-PREFIX_SIZE;
  23. /*原来内存的大小*/
  24. oldsize = *((size_t*)realptr);
  25. /*重新申请内存*/
  26. newptr = realloc(realptr,size+PREFIX_SIZE);
  27. if (!newptr) zmalloc_oom_handler(size);
  28. /*存储size*/
  29. *((size_t*)newptr) = size;
  30. /*free原来的空间*/
  31. update_zmalloc_stat_free(oldsize);
  32. /*更新全局量 used_memory*/
  33. update_zmalloc_stat_alloc(size);
  34. return (char*)newptr+PREFIX_SIZE;
  35. #endif
  36. }

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

6.zmalloc_size

  1. /* Provide zmalloc_size() for systems where this function is not provided by
  2. * malloc itself, given that in that case we store a header with this
  3. * information as the first bytes of every allocation.
  4. *
  5. */
  6. #ifndef HAVE_MALLOC_SIZE
  7. size_t zmalloc_size(void *ptr) {
  8. /* malloc的内存的大小*/
  9. /*向前偏移一个字长*/
  10. void *realptr = (char*)ptr-PREFIX_SIZE;
  11. /*获得大小*/
  12. size_t size = *((size_t*)realptr);
  13. /* Assume at least that all the allocations are padded at sizeof(long) by
  14. * the underlying allocator. */
  15. /*内存对齐*/
  16. if (size&(sizeof(long)-1)) size += sizeof(long)-(size&(sizeof(long)-1));
  17. return size+PREFIX_SIZE;
  18. }
  19. #endif

7.zstrdup

复制字符串函数

  1. char *zstrdup(const char *s) {
  2. /*多出来一个*/
  3. size_t l = strlen(s)+1;
  4. char *p = zmalloc(l);
  5. memcpy(p,s,l);
  6. return p;
  7. }

8. zmalloc_used_memory

实现了线程安全方法

  1. /*返回used_memory 线程安全*/
  2. size_t zmalloc_used_memory(void) {
  3. size_t um;
  4. if (zmalloc_thread_safe) {
  5. #ifdef HAVE_ATOMIC
  6. um = __sync_add_and_fetch(&used_memory, 0);
  7. #else
  8. /*加锁*/
  9. pthread_mutex_lock(&used_memory_mutex);
  10. um = used_memory;
  11. /*解锁*/
  12. pthread_mutex_unlock(&used_memory_mutex);
  13. #endif
  14. }/*保证线程安全*/
  15. else {
  16. um = used_memory;
  17. }
  18. return um;
  19. }

9. help functions

  1. /*
  2. * 是否需要线程安全
  3. * 0代表不需要
  4. */
  5. void zmalloc_enable_thread_safeness(void) {
  6. zmalloc_thread_safe = 1;
  7. }
  8. /* oom状态下采取的操作: out of memory
  9. * 默认为 zmalloc_default_oom()
  10. */
  11. void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
  12. zmalloc_oom_handler = oom_handler;
  13. }

10.zmalloc_get_rss

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

  1. #if defined(HAVE_PROC_STAT)
  2. #include <unistd.h>
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. size_t zmalloc_get_rss(void) {
  7. /*sysconf为系统函数,获取页大小*/
  8. int page = sysconf(_SC_PAGESIZE);
  9. size_t rss;
  10. char buf[4096];
  11. char filename[256];
  12. int fd, count;
  13. char *p, *x;
  14. /*将当前进程所对应的stat文件的绝对路径保存到filename*/
  15. snprintf(filename,256,"/proc/%d/stat",getpid());
  16. /*只读打开stat文件*/
  17. if ((fd = open(filename,O_RDONLY)) == -1) return 0;
  18. if (read(fd,buf,4096) <= 0) {
  19. close(fd);
  20. return 0;
  21. }
  22. close(fd);
  23. /*读完信息,存到buf*/
  24. p = buf;
  25. count = 23; /* RSS 是 /proc/<pid>/stat 的第24个字段*/
  26. while(p && count--) {
  27. /*查找空格,由空格分隔字段*/
  28. p = strchr(p,' ');
  29. /*指向下一个字段首地址*/
  30. if (p) p++;
  31. }
  32. if (!p) return 0;
  33. x = strchr(p,' ');
  34. if (!x) return 0;
  35. *x = '\0';
  36. /*string to long long*/
  37. rss = strtoll(p,NULL,10);
  38. /*rss获取的是内存页的页数,乘以页大小即可知*/
  39. rss *= page;
  40. return rss;
  41. }
  42. #else
  43. size_t zmalloc_get_rss(void) {
  44. /* If we can't get the RSS in an OS-specific way for this system just
  45. * return the memory usage we estimated in zmalloc()..
  46. *
  47. * Fragmentation will appear to be always 1 (no fragmentation)
  48. * of course...
  49. *
  50. * 如果不能通过操作系统来获得,就直接返回used_memory.
  51. */
  52. return zmalloc_used_memory();
  53. }
  54. #endif
  55. /*
  56. * Fragmentation = RSS / allocated-bytes
  57. * 内存碎片率
  58. */
  59. float zmalloc_get_fragmentation_ratio(size_t rss) {
  60. return (float)rss/zmalloc_used_memory();
  61. }

原文链接:http://www.cnblogs.com/oasisyang/p/14296730.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号