当前位置:   article > 正文

带你用纯C实现一个内存池(图文结合)_c内存池

c内存池

为什么要用内存池

为什么要用内存池?首先,在7 * 24h的服务器中如果不使用内存池,而使用malloc和free,那么就非常容易产生内存碎片,早晚都会申请内存失败;并且在比较复杂的代码或者继承的屎山中,非常容易出现内存泄漏导致mmo的问题。

为了解决这两个问题,内存池就应运而生了。内存池预先分配一大块内存来做一个内存池,业务中的内存分配和释放都由这个内存池来管理,内存池内的内存不足时其内部会自己申请。所以内存碎片的问题就交由内存池的算法来优化,而内存泄漏的问题只需要遵守内存池提供的api,就非常容易避免内存泄漏了。

即使出现了内存泄漏,排查的思路也很清晰。1.检查是不是内存池的问题;2.如果不是内存池的问题,就检查是不是第三方库的内存泄漏。

内存池的使用场景

  1. 全局内存池
  2. 一个连接一个内存池(本文实现这个场景的内存池)

设计一个内存池

总体介绍

由于本文是一个连接一个内存池,所以后续介绍和代码都是以4k为分界线,大于4k的我们认为是大块内存;小于4k的我们认为是小块内存。并且注意这里的4k,并不是严格遵照4096,而是在描述上,用4k比较好描述。

在真正使用内存之前,内存池提前分配一定数量且大小相等的内存块以作备用,当真正被用户调用api分配内存的时候,直接从内存块中获取内存(指小块内存),当内存块不够用了,再有内存池取申请新的内存块。而如果是需要大块内存,则内存池直接申请大块内存再返回给用户。

内存池:就是将这些提前申请的内存块组织管理起来的数据结构,内存池实现原理主要分为分配,回收,扩容三部分。

内存池原理之小块内存:分配=> 内存池预申请一块4k的内存块,这里称为block,即block=4k内存块。当用户向内存池申请内存size小于4k时,内存池从block的空间中划分出去size空间,当再有新申请时,再划分出去。扩容=> 直到block中的剩余空间不足以分配size大小,那么此时内存池会再次申请一块block,再从新的block中划分size空间给用户。回收=> 每一次申请小内存,都会在对应的block中引用计数加1,每一次释放小内存时,都会在block中引用计数减1,只有当引用计数为零的时候,才会回收block使他重新成为空闲空间,以便重复利用空间。这样,内存池避免频繁向内核申请/释放内存,从而提高系统性能。

内存池原理之大块内存:分配=> 因为大块内存是大于4k的,所以内存池不预先申请内存,也就是用户申请的时候,内存池再申请内存,然后返回给用户。扩容=> 大块内存不存在扩容。回收=> 对于大块内存来说,回收就直接free掉即可。

上面理论讲完了,下面来介绍如何管理小块内存和大块内存。

小块内存的分配与管理

在创建内存池的时候,会预先申请一块4k的内存,并且在起始处将pool的结构体和node的结构体放进去,从last开始一直到end都是空闲内存,<last , end >中间的区域就用来存储小块内存。每一次mp_malloc,就将last指针后移,直到 e n d − l a s t < s i z e end - last < size end−last<size 时,进行扩容,将新block的last后移即可。

初始状态

 分配内存

 扩容

 相关视频推荐

200行代码实现slab,开启内存池的内存管理(准备linux环境)

线程池、内存池、异步请求池、数据库连接池、无锁队列的ringbuffer,提升程序性能必备技术

5种内存泄漏检测方式,让你重新理解C++内存管理

免费学习地址:C/C++Linux服务器开发/后台架构师

需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

 

大块内存的分配与管理

对于大块内存,前面已经说了,用户申请的时候,内存池才申请

申请一块大内存

 再申请一块大内存

 

内存池代码实现

向外提供的api

  • mp_create_pool:创建一个线程池,其核心是创建struct mp_pool_s这个结构体,并申请4k内存,将各个指针指向上文初始状态的图一样。
  • mp_destroy_pool:销毁内存池,遍历小块结构体和大块结构体,进行free释放内存
  • mp_malloc:提供给用户申请内存的api
  • mp_calloc:通过mp_malloc申请内存后置零,相当于calloc
  • mp_free:释放由mp_malloc返回的内存
  • mp_reset_pool:将block的last置为初始状态,销毁所有大块内存
  • monitor_mp_poll:监控内存池状态
  1. struct mp_pool_s *mp_create_pool(size_t size);
  2. void mp_destroy_pool(struct mp_pool_s *pool);
  3. void *mp_malloc(struct mp_pool_s *pool, size_t size);
  4. void *mp_calloc(struct mp_pool_s *pool, size_t size);
  5. void mp_free(struct mp_pool_s *pool, void *p);
  6. void mp_reset_pool(struct mp_pool_s *pool);
  7. void monitor_mp_poll(struct mp_pool_s *pool, char *tk);

相关结构体的定义

mp_pool_s 就是整个内存池的管理结构,我们做的内存池是一个连接一个内存池,所以对于整个程序而言,内存池对象是有很多个的。

可能读者会有疑问,有了head,为什么还有current,是因为如果一个block剩余空间小于size超过一定次数后,将current指向下一个block,这样就加快内存分配效率,减少遍历次数。

  1. //4k一block结点
  2. struct mp_node_s {
  3. unsigned char *end;//块的结尾
  4. unsigned char *last;//使用到哪了
  5. struct mp_node_s *next;//链表
  6. int quote;//引用计数
  7. int failed;//失效次数
  8. };
  9. struct mp_large_s {
  10. struct mp_large_s *next;//链表
  11. int size;//alloc的大小
  12. void *alloc;//大块内存的起始地址
  13. };
  14. struct mp_pool_s {
  15. struct mp_large_s *large;
  16. struct mp_node_s *head;
  17. struct mp_node_s *current;
  18. };

内存对齐

访问速度是内存对齐的原因之一,另外一个原因是某些平台(arm)不支持未内存对齐的访问

在4k里面划分内存,那么必然有很多地方是不对齐的,所以这里提供两个内存对齐的函数。那么为什么要内存对齐呢?其一:提高访问速度;其二:某些平台arm不支持未对其的内存访问,会出错。

  1. #define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
  2. #define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))

创建与销毁内存池

创建一个线程池,其核心是创建struct mp_pool_s这个结构体,并申请4k内存,将各个指针指向上文初始状态的图一样。
销毁内存池,遍历小块结构体和大块结构体,进行free释放内存。

  1. //创建内存池
  2. struct mp_pool_s *mp_create_pool(size_t size) {
  3. struct mp_pool_s *pool;
  4. if (size < PAGE_SIZE || size % PAGE_SIZE != 0) {
  5. size = PAGE_SIZE;
  6. }
  7. //分配4k以上不用malloc,用posix_memalign
  8. /*
  9. int posix_memalign (void **memptr, size_t alignment, size_t size);
  10. */
  11. int ret = posix_memalign((void **) &pool, MP_ALIGNMENT, size); //4K + mp_pool_s
  12. if (ret) {
  13. return NULL;
  14. }
  15. pool->large = NULL;
  16. pool->current = pool->head = (unsigned char *) pool + sizeof(struct mp_pool_s);
  17. pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
  18. pool->head->end = (unsigned char *) pool + PAGE_SIZE;
  19. pool->head->failed = 0;
  20. return pool;
  21. }
  22. //销毁内存池
  23. void mp_destroy_pool(struct mp_pool_s *pool) {
  24. struct mp_large_s *large;
  25. for (large = pool->large; large; large = large->next) {
  26. if (large->alloc) {
  27. free(large->alloc);
  28. }
  29. }
  30. struct mp_node_s *cur, *next;
  31. cur = pool->head->next;
  32. while (cur) {
  33. next = cur->next;
  34. free(cur);
  35. cur = next;
  36. }
  37. free(pool);
  38. }

 

提供给用户的内存申请api

申请的内存以size做区分,如果大于4k就分配大块内存,小于4k就去block里面划分。

  1. //分配内存
  2. void *mp_malloc(struct mp_pool_s *pool, size_t size) {
  3. if (size <= 0) {
  4. return NULL;
  5. }
  6. if (size > PAGE_SIZE - sizeof(struct mp_node_s)) {
  7. //large
  8. return mp_malloc_large(pool, size);
  9. }
  10. else {
  11. //small
  12. unsigned char *mem_addr = NULL;
  13. struct mp_node_s *cur = NULL;
  14. cur = pool->current;
  15. while (cur) {
  16. mem_addr = mp_align_ptr(cur->last, MP_ALIGNMENT);
  17. if (cur->end - mem_addr >= size) {
  18. cur->quote++;//引用+1
  19. cur->last = mem_addr + size;
  20. return mem_addr;
  21. }
  22. else {
  23. cur = cur->next;
  24. }
  25. }
  26. return mp_malloc_block(pool, size);// open new space
  27. }
  28. }
  29. void *mp_calloc(struct mp_pool_s *pool, size_t size) {
  30. void *mem_addr = mp_malloc(pool, size);
  31. if (mem_addr) {
  32. memset(mem_addr, 0, size);
  33. }
  34. return mem_addr;
  35. }

小块内存block扩容

所有的block都 e n d − l a s t < s i z e end - last < size end−last<size 时,进行扩容,将新block的last后移即可。

  1. //new block 4k
  2. void *mp_malloc_block(struct mp_pool_s *pool, size_t size) {
  3. unsigned char *block;
  4. int ret = posix_memalign((void **) &block, MP_ALIGNMENT, PAGE_SIZE); //4K
  5. if (ret) {
  6. return NULL;
  7. }
  8. struct mp_node_s *new_node = (struct mp_node_s *) block;
  9. new_node->end = block + PAGE_SIZE;
  10. new_node->next = NULL;
  11. unsigned char *ret_addr = mp_align_ptr(block + sizeof(struct mp_node_s), MP_ALIGNMENT);
  12. new_node->last = ret_addr + size;
  13. new_node->quote++;
  14. struct mp_node_s *current = pool->current;
  15. struct mp_node_s *cur = NULL;
  16. for (cur = current; cur->next; cur = cur->next) {
  17. if (cur->failed++ > 4) {
  18. current = cur->next;
  19. }
  20. }
  21. //now cur = last node
  22. cur->next = new_node;
  23. pool->current = current;
  24. return ret_addr;
  25. }

分配大块内存

  1. //size>4k
  2. void *mp_malloc_large(struct mp_pool_s *pool, size_t size) {
  3. unsigned char *big_addr;
  4. int ret = posix_memalign((void **) &big_addr, MP_ALIGNMENT, size); //size
  5. if (ret) {
  6. return NULL;
  7. }
  8. struct mp_large_s *large;
  9. //released struct large resume
  10. int n = 0;
  11. for (large = pool->large; large; large = large->next) {
  12. if (large->alloc == NULL) {
  13. large->size = size;
  14. large->alloc = big_addr;
  15. return big_addr;
  16. }
  17. if (n++ > 3) {
  18. break;// 为了避免过多的遍历,限制次数
  19. }
  20. }
  21. large = mp_malloc(pool, sizeof(struct mp_large_s));
  22. if (large == NULL) {
  23. free(big_addr);
  24. return NULL;
  25. }
  26. large->size = size;
  27. large->alloc = big_addr;
  28. large->next = pool->large;
  29. pool->large = large;
  30. return big_addr;
  31. }

释放内存

如果是大块内存,找到之后直接释放;如果是小块内存,将引用计数减1,如果引用计数为0则重置last。

  1. //释放内存
  2. void mp_free(struct mp_pool_s *pool, void *p) {
  3. struct mp_large_s *large;
  4. for (large = pool->large; large; large = large->next) {//大块
  5. if (p == large->alloc) {
  6. free(large->alloc);
  7. large->size = 0;
  8. large->alloc = NULL;
  9. return;
  10. }
  11. }
  12. //小块 引用-1
  13. struct mp_node_s *cur = NULL;
  14. for (cur = pool->head; cur; cur = cur->next) {
  15. // printf("cur:%p p:%p end:%p\n", (unsigned char *) cur, (unsigned char *) p, (unsigned char *) cur->end);
  16. if ((unsigned char *) cur <= (unsigned char *) p && (unsigned char *) p <= (unsigned char *) cur->end) {
  17. cur->quote--;
  18. if (cur->quote == 0) {
  19. if (cur == pool->head) {
  20. pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
  21. }
  22. else {
  23. cur->last = (unsigned char *) cur + sizeof(struct mp_node_s);
  24. }
  25. cur->failed = 0;
  26. pool->current = pool->head;
  27. }
  28. return;
  29. }
  30. }
  31. }

内存池测试

  1. //
  2. // Created by 68725 on 2022/7/26.
  3. //
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <string.h>
  7. #define PAGE_SIZE 4096
  8. #define MP_ALIGNMENT 16
  9. #define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
  10. #define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
  11. //4k一block结点
  12. struct mp_node_s {
  13. unsigned char *end;//块的结尾
  14. unsigned char *last;//使用到哪了
  15. struct mp_node_s *next;//链表
  16. int quote;//引用计数
  17. int failed;//失效次数
  18. };
  19. struct mp_large_s {
  20. struct mp_large_s *next;//链表
  21. int size;//alloc的大小
  22. void *alloc;//大块内存的起始地址
  23. };
  24. struct mp_pool_s {
  25. struct mp_large_s *large;
  26. struct mp_node_s *head;
  27. struct mp_node_s *current;
  28. };
  29. struct mp_pool_s *mp_create_pool(size_t size);
  30. void mp_destroy_pool(struct mp_pool_s *pool);
  31. void *mp_malloc(struct mp_pool_s *pool, size_t size);
  32. void *mp_calloc(struct mp_pool_s *pool, size_t size);
  33. void mp_free(struct mp_pool_s *pool, void *p);
  34. void mp_reset_pool(struct mp_pool_s *pool);
  35. void monitor_mp_poll(struct mp_pool_s *pool, char *tk);
  36. void mp_reset_pool(struct mp_pool_s *pool) {
  37. struct mp_node_s *cur;
  38. struct mp_large_s *large;
  39. for (large = pool->large; large; large = large->next) {
  40. if (large->alloc) {
  41. free(large->alloc);
  42. }
  43. }
  44. pool->large = NULL;
  45. pool->current = pool->head;
  46. for (cur = pool->head; cur; cur = cur->next) {
  47. cur->last = (unsigned char *) cur + sizeof(struct mp_node_s);
  48. cur->failed = 0;
  49. cur->quote = 0;
  50. }
  51. }
  52. //创建内存池
  53. struct mp_pool_s *mp_create_pool(size_t size) {
  54. struct mp_pool_s *pool;
  55. if (size < PAGE_SIZE || size % PAGE_SIZE != 0) {
  56. size = PAGE_SIZE;
  57. }
  58. //分配4k以上不用malloc,用posix_memalign
  59. /*
  60. int posix_memalign (void **memptr, size_t alignment, size_t size);
  61. */
  62. int ret = posix_memalign((void **) &pool, MP_ALIGNMENT, size); //4K + mp_pool_s
  63. if (ret) {
  64. return NULL;
  65. }
  66. pool->large = NULL;
  67. pool->current = pool->head = (unsigned char *) pool + sizeof(struct mp_pool_s);
  68. pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
  69. pool->head->end = (unsigned char *) pool + PAGE_SIZE;
  70. pool->head->failed = 0;
  71. return pool;
  72. }
  73. //销毁内存池
  74. void mp_destroy_pool(struct mp_pool_s *pool) {
  75. struct mp_large_s *large;
  76. for (large = pool->large; large; large = large->next) {
  77. if (large->alloc) {
  78. free(large->alloc);
  79. }
  80. }
  81. struct mp_node_s *cur, *next;
  82. cur = pool->head->next;
  83. while (cur) {
  84. next = cur->next;
  85. free(cur);
  86. cur = next;
  87. }
  88. free(pool);
  89. }
  90. //size>4k
  91. void *mp_malloc_large(struct mp_pool_s *pool, size_t size) {
  92. unsigned char *big_addr;
  93. int ret = posix_memalign((void **) &big_addr, MP_ALIGNMENT, size); //size
  94. if (ret) {
  95. return NULL;
  96. }
  97. struct mp_large_s *large;
  98. //released struct large resume
  99. int n = 0;
  100. for (large = pool->large; large; large = large->next) {
  101. if (large->alloc == NULL) {
  102. large->size = size;
  103. large->alloc = big_addr;
  104. return big_addr;
  105. }
  106. if (n++ > 3) {
  107. break;// 为了避免过多的遍历,限制次数
  108. }
  109. }
  110. large = mp_malloc(pool, sizeof(struct mp_large_s));
  111. if (large == NULL) {
  112. free(big_addr);
  113. return NULL;
  114. }
  115. large->size = size;
  116. large->alloc = big_addr;
  117. large->next = pool->large;
  118. pool->large = large;
  119. return big_addr;
  120. }
  121. //new block 4k
  122. void *mp_malloc_block(struct mp_pool_s *pool, size_t size) {
  123. unsigned char *block;
  124. int ret = posix_memalign((void **) &block, MP_ALIGNMENT, PAGE_SIZE); //4K
  125. if (ret) {
  126. return NULL;
  127. }
  128. struct mp_node_s *new_node = (struct mp_node_s *) block;
  129. new_node->end = block + PAGE_SIZE;
  130. new_node->next = NULL;
  131. unsigned char *ret_addr = mp_align_ptr(block + sizeof(struct mp_node_s), MP_ALIGNMENT);
  132. new_node->last = ret_addr + size;
  133. new_node->quote++;
  134. struct mp_node_s *current = pool->current;
  135. struct mp_node_s *cur = NULL;
  136. for (cur = current; cur->next; cur = cur->next) {
  137. if (cur->failed++ > 4) {
  138. current = cur->next;
  139. }
  140. }
  141. //now cur = last node
  142. cur->next = new_node;
  143. pool->current = current;
  144. return ret_addr;
  145. }
  146. //分配内存
  147. void *mp_malloc(struct mp_pool_s *pool, size_t size) {
  148. if (size <= 0) {
  149. return NULL;
  150. }
  151. if (size > PAGE_SIZE - sizeof(struct mp_node_s)) {
  152. //large
  153. return mp_malloc_large(pool, size);
  154. }
  155. else {
  156. //small
  157. unsigned char *mem_addr = NULL;
  158. struct mp_node_s *cur = NULL;
  159. cur = pool->current;
  160. while (cur) {
  161. mem_addr = mp_align_ptr(cur->last, MP_ALIGNMENT);
  162. if (cur->end - mem_addr >= size) {
  163. cur->quote++;//引用+1
  164. cur->last = mem_addr + size;
  165. return mem_addr;
  166. }
  167. else {
  168. cur = cur->next;
  169. }
  170. }
  171. return mp_malloc_block(pool, size);// open new space
  172. }
  173. }
  174. void *mp_calloc(struct mp_pool_s *pool, size_t size) {
  175. void *mem_addr = mp_malloc(pool, size);
  176. if (mem_addr) {
  177. memset(mem_addr, 0, size);
  178. }
  179. return mem_addr;
  180. }
  181. //释放内存
  182. void mp_free(struct mp_pool_s *pool, void *p) {
  183. struct mp_large_s *large;
  184. for (large = pool->large; large; large = large->next) {//大块
  185. if (p == large->alloc) {
  186. free(large->alloc);
  187. large->size = 0;
  188. large->alloc = NULL;
  189. return;
  190. }
  191. }
  192. //小块 引用-1
  193. struct mp_node_s *cur = NULL;
  194. for (cur = pool->head; cur; cur = cur->next) {
  195. // printf("cur:%p p:%p end:%p\n", (unsigned char *) cur, (unsigned char *) p, (unsigned char *) cur->end);
  196. if ((unsigned char *) cur <= (unsigned char *) p && (unsigned char *) p <= (unsigned char *) cur->end) {
  197. cur->quote--;
  198. if (cur->quote == 0) {
  199. if (cur == pool->head) {
  200. pool->head->last = (unsigned char *) pool + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
  201. }
  202. else {
  203. cur->last = (unsigned char *) cur + sizeof(struct mp_node_s);
  204. }
  205. cur->failed = 0;
  206. pool->current = pool->head;
  207. }
  208. return;
  209. }
  210. }
  211. }
  212. void monitor_mp_poll(struct mp_pool_s *pool, char *tk) {
  213. printf("\r\n\r\n------start monitor poll------%s\r\n\r\n", tk);
  214. struct mp_node_s *head = NULL;
  215. int i = 0;
  216. for (head = pool->head; head; head = head->next) {
  217. i++;
  218. if (pool->current == head) {
  219. printf("current==>第%d块\n", i);
  220. }
  221. if (i == 1) {
  222. printf("第%02d块small block 已使用:%4ld 剩余空间:%4ld 引用:%4d failed:%4d\n", i,
  223. (unsigned char *) head->last - (unsigned char *) pool,
  224. head->end - head->last, head->quote, head->failed);
  225. }
  226. else {
  227. printf("第%02d块small block 已使用:%4ld 剩余空间:%4ld 引用:%4d failed:%4d\n", i,
  228. (unsigned char *) head->last - (unsigned char *) head,
  229. head->end - head->last, head->quote, head->failed);
  230. }
  231. }
  232. struct mp_large_s *large;
  233. i = 0;
  234. for (large = pool->large; large; large = large->next) {
  235. i++;
  236. if (large->alloc != NULL) {
  237. printf("第%d块large block size=%d\n", i, large->size);
  238. }
  239. }
  240. printf("\r\n\r\n------stop monitor poll------\r\n\r\n");
  241. }
  242. int main() {
  243. struct mp_pool_s *p = mp_create_pool(PAGE_SIZE);
  244. monitor_mp_poll(p, "create memory pool");
  245. #if 0
  246. printf("mp_align(5, %d): %d, mp_align(17, %d): %d\n", MP_ALIGNMENT, mp_align(5, MP_ALIGNMENT), MP_ALIGNMENT,
  247. mp_align(17, MP_ALIGNMENT));
  248. printf("mp_align_ptr(p->current, %d): %p, p->current: %p\n", MP_ALIGNMENT, mp_align_ptr(p->current, MP_ALIGNMENT),
  249. p->current);
  250. #endif
  251. void *mp[30];
  252. int i;
  253. for (i = 0; i < 30; i++) {
  254. mp[i] = mp_malloc(p, 512);
  255. }
  256. monitor_mp_poll(p, "申请512字节30个");
  257. for (i = 0; i < 30; i++) {
  258. mp_free(p, mp[i]);
  259. }
  260. monitor_mp_poll(p, "销毁512字节30个");
  261. int j;
  262. for (i = 0; i < 50; i++) {
  263. char *pp = mp_calloc(p, 32);
  264. for (j = 0; j < 32; j++) {
  265. if (pp[j]) {
  266. printf("calloc wrong\n");
  267. exit(-1);
  268. }
  269. }
  270. }
  271. monitor_mp_poll(p, "申请32字节50个");
  272. for (i = 0; i < 50; i++) {
  273. char *pp = mp_malloc(p, 3);
  274. }
  275. monitor_mp_poll(p, "申请3字节50个");
  276. void *pp[10];
  277. for (i = 0; i < 10; i++) {
  278. pp[i] = mp_malloc(p, 5120);
  279. }
  280. monitor_mp_poll(p, "申请大内存5120字节10个");
  281. for (i = 0; i < 10; i++) {
  282. mp_free(p, pp[i]);
  283. }
  284. monitor_mp_poll(p, "销毁大内存5120字节10个");
  285. mp_reset_pool(p);
  286. monitor_mp_poll(p, "reset pool");
  287. for (i = 0; i < 100; i++) {
  288. void *s = mp_malloc(p, 256);
  289. }
  290. monitor_mp_poll(p, "申请256字节100个");
  291. mp_destroy_pool(p);
  292. return 0;
  293. }

nginx内存池对比分析

相关结构体定义对比

创建内存池对比

内存申请对比

 

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号