zoukankan      html  css  js  c++  java
  • haproxy-代码阅读-内存管理

    haproxy内存池概述

    内存池按照类型分类,每个类型的内存池都有一个名字,用链表记录空闲的内存块,每个内存块大小相等,并按照16字节对齐。
    haporxy用pool_head 结构记录内存池

    struct pool_head {
    	void **free_list;   /* 空闲链表 */
    	struct list list;	/* 双向链表,链接每种类型的内存池 */
    	unsigned int used;	/* 使用了多少内存块 */
    	unsigned int allocated;	/* 分配了多少内存块 */
    	unsigned int limit;	/* 内存块上限 */
    	unsigned int minavail;	/* 最少保留几个,回收时不会全部回收 */
    	unsigned int size;	/* 内存块大小 */
    	unsigned int flags;	/* 能否共享,类型不同,但大小相同的,能否共享一个pool_head */
    	unsigned int users;	/* 内存池有几个使用者 */
    	char name[12];		/* 内存池名称 */
    };
    

    在程序执行过程中,产生的内存池,很有可能按照大小,排列成如下方式:

    内存池的创建

    haproxy创建内存池时,会先检查内存池中,有没有与所需大小相同的内存池,有且内存池可共享,将pool_head.users++。若没有,则新创建一个内存池。

    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;
    	
    	//按照16字节对齐
    	align = 16;
    	size  = (size + align - 1) & -align;
    	//pools是全局变量,内存池的头节点
    	start = &pools;
    	pool = NULL;
    
    	
    	list_for_each_entry(entry, &pools, list) {
    		if (entry->size == size) {
    			if (flags & entry->flags & MEM_F_SHARED) {//大小相等且可共享
    				pool = entry;
    				break;
    			}
    		}
    		else if (entry->size > size) { //内存池按照大小排序,新pool_head,插在适当位置
    			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;
    		LIST_ADDQ(start, &pool->list);
    	}
    	pool->users++;
    	return pool;
    }
    

    内存申请

    create_pool仅仅是申请了内存池的类型,还没有具体分配内存,分配内存的工作由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);
    	//如果申请失败,pool_gc2()垃圾回收,然后再申请一次,再失败就放弃
    	if (!ret) {
    		pool_gc2(); 
    		ret = MALLOC(pool->size);
    		if (!ret)
    			return NULL;
    	}
    	pool->allocated++;
    	pool->used++;
    	return ret;
    }
    

    其中,pool_gc2()垃圾回收函数,会遍历所有内存池,并释放空闲内存块(留下minavail的数量)。
    使用者申请内存,不是直接调用pool_refill_alloc,而是通过调用pool_alloc2,如果free_list中没有空闲内存了,则调用pool_refill_alloc申请下内存。如果还有空闲内存,则使用第一块内存,free_list指向下一块。

    #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;                                                  
    })
    

    内存释放

    如果内存块的使用者,在申请内存块后,不主动释放内存块,那么销毁内存池后,内存块将无法回到内存。所以,必须注意,要主动释放内存块。
    内存块的释放很简单,将内存块直接放到空闲链表的第一个节点就行。

    #define pool_free2(pool, ptr)                           
    ({                                                      
            *(void **)ptr = (void *)pool->free_list;        
            pool->free_list = (void *)ptr;                  
            pool->used--;                                   
            pool_gc2_ifneed(pool);                          
    })
    

    销毁内存池

    内存池的销毁很简单,释放所有空闲内存块,然后释放内存池。如果还有使用中的内存(pool->used != 0),停止释放

    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_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;
    }
    
  • 相关阅读:
    php基础之简单运算
    选择平淡
    php基础之控制结构
    关于三元运算符的初步应用及理解
    VS2015 遇到异常。这可能是由某个扩展导致的
    C#中如何去除窗体默认的关闭按钮
    (转载)SQL基础--> 约束(CONSTRAINT)
    SQL Server安装后设置SQL Server验证登录
    附加数据库 对于 ""失败,无法打开物理文件 操作系统错误 5:拒绝访问 SQL Sever
    SQL Server数据库操作(二)
  • 原文地址:https://www.cnblogs.com/shenlinken/p/6917509.html
Copyright © 2011-2022 走看看