沉淀之log4c的malloc

    内存分配函数的接口,log4c内部又重新的封装了一下,在内部实现上仍然是采用的系统内存分配的那一套调用接口。重新实现这么一套接口,应该是为了方便调试,保证易用性和稳定下有了提高。
    这个封装的内部在分配了之后对于返回值做了检查。

malloc.h
  1. #ifndef __sd_malloc_h
  2. #define __sd_malloc_h
  3. #include <stddef.h>
  4. #include <stdlib.h>
  5. #include "defs.h"
  6. /**
  7. * @file malloc.h
  8. */
  9. __SD_BEGIN_DECLS
  10. typedef void (*sd_malloc_handler_t)();
  11. extern sd_malloc_handler_t sd_malloc_set_handler(void (*a_handler)());
  12. #ifndef __SD_DEBUG__
  13. extern void *sd_malloc(size_t n);
  14. extern void *sd_calloc(size_t n, size_t s);
  15. extern void *sd_realloc(void *p, size_t n);
  16. extern char *sd_strdup (const char *__str);
  17. #else
  18. #define sd_malloc malloc
  19. #define sd_calloc calloc
  20. #define sd_realloc realloc
  21. #define sd_strdup strdup
  22. #endif
  23. __SD_END_DECLS
  24. #endif
    头文件中一样包含着一对__SD_BEGIN_DECLS和__SD_END_DECLS,以及头文件defs.h的包含。
使用typedef定义了一个函数类型:typedef void (*sd_malloc_handler_t)();    标识sd_malloc_handler_t代表一个函数类型,
用这个类型可以定义类似如下结构的函数变量:
    void test()
然后是根据一个宏:__SD_DEBUG__作为编译开关进行选择两套内存申请函数,一个是用宏进行定义的接口,实际就是系统本身的内存操作接口。另一个是自行实现的一组函数:sd_malloc、sd_calloc、sd_realloc、sd_strdup。

malloc.c里面就是对于在debug模式下的时候所实现的头文件中声明的这一组函数。

malloc.c
  1. #ifdef HAVE_CONFIG_H
  2. #include "config.h"
  3. #endif
  4. #include <stdio.h>
  5. #ifdef HAVE_UNISTD_H
  6. #include <unistd.h>
  7. #endif
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include "error.h"
  11. #if defined(__APPLE__)
  12. # include <sys/time.h>
  13. # include <crt_externs.h>
  14. # define environ (*_NSGetEnviron())
  15. #endif /* __APPLE__ */
  16. typedef void (*sd_malloc_handler_t)();
  17. static sd_malloc_handler_t handler = NULL;
  18. static void *
  19. fixup_null_alloc (n)
  20. size_t n;
  21. {
  22. void* p = 0;
  23. #ifdef HAVE_SBRK
  24. static char* first_break = NULL;
  25. if (n == 0)
  26. p = malloc ((size_t) 1);
  27. if (p == 0) {
  28. extern char **environ;
  29. size_t allocated;
  30. if (first_break != NULL)
  31. allocated = (char *) sbrk (0) - first_break;
  32. else
  33. allocated = (char *) sbrk (0) - (char *) &environ;
  34. sd_error(" Cannot allocate %lu bytes after allocating %lu bytes ",
  35. (unsigned long) n, (unsigned long) allocated);
  36. if (handler)
  37. handler();
  38. else {
  39. sd_error(" Memory exhausted !! Aborting. ");
  40. abort();
  41. }
  42. }
  43. #endif
  44. return p;
  45. }
  46. sd_malloc_handler_t
  47. sd_malloc_set_handler(a_handler)
  48. sd_malloc_handler_t a_handler;
  49. {
  50. sd_malloc_handler_t previous = handler;
  51. handler = a_handler;
  52. return previous;
  53. }
  54. /* Allocate N bytes of memory dynamically, with error checking. */
  55. void *
  56. sd_malloc (n)
  57. size_t n;
  58. {
  59. void *p;
  60. p = malloc (n);
  61. if (p == 0)
  62. p = fixup_null_alloc (n);
  63. return p;
  64. }
  65. /* Allocate memory for N elements of S bytes, with error checking. */
  66. void *
  67. sd_calloc (n, s)
  68. size_t n, s;
  69. {
  70. void *p;
  71. p = calloc (n, s);
  72. if (p == 0)
  73. p = fixup_null_alloc (n * s);
  74. return p;
  75. }
  76. /* Change the size of an allocated block of memory P to N bytes,
  77. with error checking.
  78. If P is NULL, run sd_malloc. */
  79. void *
  80. sd_realloc (p, n)
  81. void *p;
  82. size_t n;
  83. {
  84. if (p == 0)
  85. return sd_malloc (n);
  86. p = realloc (p, n);
  87. if (p == 0)
  88. p = fixup_null_alloc (n);
  89. return p;
  90. }
  91. /* Return a newly allocated copy of STRING. */
  92. char *
  93. sd_strdup (string)
  94. const char *string;
  95. {
  96. return strcpy (sd_malloc (strlen (string) + 1), string);
  97. }


    其实每个函数的实现里面,都是先调用系统函数进行内存分配,当内存分配不成功的时候会调用fixup_null_alloc函数进行再次尝试。熟悉了ansi C的同学看着这里的函数的定义会觉得比较怪异,其实Ansi C之前的函数形式就是这样的(K&R C风格),Ansi C为了兼容性也实现支持这种格式。参数还需要再这里重新定义下;
    然后对于fixup_null_alloc的实现着重的研究下,因为下面所有函数的实现在失败的时候都会去调用这个函数,然后另外还有一个函数就是sd_malloc_set_handler,我在log4c里面搜索了一下,这个函数没有被调用的地方,也没有文字性的说明,搞不懂到底是要做什么,所以暂时不进行分析。

    fixup_null_alloc函数的定义在上面的第二十五行到第五十六行。其中第二十八行到三十五行在传入参数为0的时候做了一次判断并根据判断进行调用系统函数进行内存的分配。接下来的三十六行到五十三行是利用sbrk系统调用获取而这里参数给的是0,可以利用这个调用获取当前program break的位置。然后进行计算,打印一下当前系统中内存的情况进行报错处理。
    至于为什么用sbrk (0) - first_break;或者用sbrk (0) - (char *) &environ;
    首先使用sbrk (0)能够知道当前系统中可用堆的结束的位置,然后static关键字声明的first_break在bss段的最后,两者相减就能得到一共有多少内存已经分配出去了。至于environ为什么,还没有理解。

    至于使用方式上就拿这一组当成系统的内存操作接口就可以了。示例程序就不啰嗦了。

    











原文地址:https://www.cnblogs.com/cfzhang/p/03c1c19c4d9a8e0a667f52da67bcd1cd.html