Haproxy代码分析系列:内存管理
2012-07-21 22:01
337 查看
Haproxy实现了自己的内存管理,主要思路是为经常使用的数据结构维护一个内存池,把所有的内存池再串起来,申请和释放内存时,首先到该内存池链表中查找该类型的内存池是否有空闲内存,有的话直接使用,没有的话再重新分配。
代码主要在src/Memory.c中,文章来源于:
Haproxy中的内存池结构是:
used,allocated,limit,minavail,size,users,是某个具体内存池使用情况的计数器,flags可以设置该内存池是否可以共享使用(MEM_F_SHARED)。
haproxy的list结构比较简单,是一个双向链表,通过list数据结构达到串起来的目的。
因此haproxy的内存池数据结构如下:
pool创建
分配一个内存池时,需要指定名称name,大小size和标记flag。
pools是一个全局变量,指向内存池链表的头,遍历内存池链表,如果有块大小相同的并且可以共用的内存池,更新users数目,如果没有可共用的内存池,则新建一个名为name的内存池,设置size,name和users=1。
注意,内存池链表按照size从小到大排列,每次会调整头pools的位置,在适合的位置插入新的pool。
pool销毁
free_list中释放后,如果该pool还有内存在被使用(used != 0),则返回,否则判断该pool是否有其它数据结构共享,如果users为0,则彻底删除该pool
内存申请
内存释放
释放时,将该内存放到该pool的空闲列表,注意此处把free_list的地址写到的要free的空间的头部,然后把free_list指向了该内存地址ptr,,并不真正的free,而是把空间保留下来已被后用,这也是haproxy的一个策略。
注:文章对http://blog.chinaunix.net/uid-10249062-id-163274.html做了扩充,多贴了一些代码。
--end
代码主要在src/Memory.c中,文章来源于:
Haproxy中的内存池结构是:
struct pool_head { void **free_list; struct list list; /* list of all known pools */ unsigned int used; /* how many chunks are currently in use */ unsigned int allocated; /* how many chunks have been allocated */ unsigned int limit; /* hard limit on the number of chunks */ unsigned int minavail; /* how many chunks are expected to be used */ unsigned int size; /* chunk size */ unsigned int flags; /* MEM_F_* */ unsigned int users; /* number of pools sharing this zone */ char name[12]; /* name of the pool */ };
used,allocated,limit,minavail,size,users,是某个具体内存池使用情况的计数器,flags可以设置该内存池是否可以共享使用(MEM_F_SHARED)。
haproxy的list结构比较简单,是一个双向链表,通过list数据结构达到串起来的目的。
struct list { struct list *n; /* next */ struct list *p; /* prev */ };
因此haproxy的内存池数据结构如下:
pool创建
struct pool_head *create_pool(char *name, unsigned int size, unsigned int flags) { struct pool_head *pool; struct pool_head *entry; struct list *start; unsigned int align; /* We need to store at least a (void *) in the chunks. Since we know * that the malloc() function will never return such a small size, * let's round the size up to something slightly bigger, in order to * ease merging of entries. Note that the rounding is a power of two. */ align = 16; size = (size + align - 1) & -align; start = &pools; pool = NULL; list_for_each_entry(entry, &pools, list) { if (entry->size == size) { /* either we can share this place and we take it, or * we look for a sharable one or for the next position * before which we will insert a new one. */ if (flags & entry->flags & MEM_F_SHARED) { /* we can share this one */ pool = entry; DPRINTF(stderr, "Sharing %s with %s\n", name, pool->name); break; } } else if (entry->size > size) { /* insert before this one */ start = &entry->list; break; } } if (!pool) { pool = CALLOC(1, sizeof(*pool)); if (!pool) return NULL; if (name) strlcpy2(pool->name, name, sizeof(pool->name)); pool->size = size; pool->flags = flags; #ifdef USE_MEM_POOL3 LIST_INIT(&pool->free_block_list); #endif LIST_ADDQ(start, &pool->list); } pool->users++; return pool; }
分配一个内存池时,需要指定名称name,大小size和标记flag。
pools是一个全局变量,指向内存池链表的头,遍历内存池链表,如果有块大小相同的并且可以共用的内存池,更新users数目,如果没有可共用的内存池,则新建一个名为name的内存池,设置size,name和users=1。
注意,内存池链表按照size从小到大排列,每次会调整头pools的位置,在适合的位置插入新的pool。
pool销毁
void *pool_destroy2(struct pool_head *pool) { if (pool) { pool_flush2(pool); if (pool->used) return pool; pool->users--; if (!pool->users) { LIST_DEL(&pool->list); FREE(pool); } } return NULL; }内存池销毁时,指定一个pool,首先free该pool的freelist,通过pool_flush2实现:
void pool_flush2(struct pool_head *pool) { void *temp, *next; if (!pool) return; next = pool->free_list; while (next) { temp = next; next = *(void **)temp; pool->allocated--; FREE(temp); } pool->free_list = next; /* here, we should have pool->allocate == pool->used */ }
free_list中释放后,如果该pool还有内存在被使用(used != 0),则返回,否则判断该pool是否有其它数据结构共享,如果users为0,则彻底删除该pool
内存申请
#define pool_alloc2(pool) \ ({ \ void *__p; \ if ((__p = pool->free_list) == NULL) \ __p = pool_refill_alloc(pool); \ else { \ pool->free_list = *(void **)pool->free_list; \ pool->used++; \ } \ __p; \ })首先从pool的free_list中取得第一个空闲内存块,分配之,注意此处直接把free_list的下一个空闲块的地址写在的上一个空间开头处,相当于实现了next的机制;如果为空,则调用pool_refill_alloc进行释放和重新分配:
void *pool_refill_alloc(struct pool_head *pool) { void *ret; if (pool->limit && (pool->allocated >= pool->limit)) return NULL; ret = MALLOC(pool->size); if (!ret) { pool_gc2(); ret = MALLOC(pool->size); if (!ret) return NULL; } pool->allocated++; pool->used++; return ret; }其中pool_gc2是对pools所有的内存池的free_list进行相应的free,以空出新的内存供MALLOC。
内存释放
#define pool_free2(pool, ptr) \ ({ \ if (likely((ptr) != NULL)) { \ *(void **)ptr = (void *)pool->free_list;\ pool->free_list = (void *)ptr; \ pool->used--; \ } \ })
释放时,将该内存放到该pool的空闲列表,注意此处把free_list的地址写到的要free的空间的头部,然后把free_list指向了该内存地址ptr,,并不真正的free,而是把空间保留下来已被后用,这也是haproxy的一个策略。
注:文章对http://blog.chinaunix.net/uid-10249062-id-163274.html做了扩充,多贴了一些代码。
--end
相关文章推荐
- Haproxy代码分析系列-一些小的Tips
- 【高效代码调错系列之静态代码分析】——将错误扼杀在摇篮
- 《Java & Ruby 代码分析系列》之基本的类实例对比
- Ajax.Dll代码分析系列一
- Linux-0.11内核源码分析系列:内存管理free_page()与free_page_tables()函数分析
- Linux-0.11内核源码分析系列:内存管理copy_page_tables()函数分析
- OC MRC之set方法内存管理(代码分析)
- Linux-0.11内核源码分析系列:内存管理try_to_share()与share_page()函数分析
- 彩翼系列-彩票分析软件源代码(双色球,排三,排五,3D,22选5,30选7)源代码
- Linux IPv4代码分析系列(2)
- Linux IPv4代码分析系列(1)
- Apache内存管理-内存池介绍与内存结点介绍(参照网络上其他资料与代码全景分析)
- 周立功lpc21xx/lpc22xx系列ARM7启动代码分析
- U-Boot系列之三:u-boot整体结构及启动代码分析(有加入一些新东西)
- linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)
- Linux IPv4代码分析系列(2)
- NSQ系列之nsqlookupd代码分析一(初探nsqlookup)
- SpringBoot系列三:SpringBoot基本概念(统一父 pom 管理、SpringBoot 代码测试、启动注解分析、配置访问路径、使用内置对象、项目打包发布)
- 代码分析系列 内存执行过程