江苏连云港做网站,网站平台建设方案策划书,企石东莞网站建设,郑州网站建设技术外包一、概述 这篇博客当中#xff0c;我们从头设计了一个基于显式空闲链表的定长内存池作为基础#xff0c;对于一般kv存储的应用场景而言#xff08;比如长-短连接映射#xff09;并无问题#xff0c;可是如果我们需要插入一篇内容较多的博客呢#xff1f;如何设计可以兼顾…一、概述这篇博客当中我们从头设计了一个基于显式空闲链表的定长内存池作为基础对于一般kv存储的应用场景而言比如长-短连接映射并无问题可是如果我们需要插入一篇内容较多的博客呢如何设计可以兼顾小内存快速随取随用的高性能需求也能适配大value插入需求。最大化减少malloc系统调用的同时又避免内存碎片的问题呢当value的值非常大的时候超过了定长内存池的block_size需要设计专门的大内存分配模块来支持它大内存请求如阈值设置为4kb直接使用malloc动态分配由large链表进行管理对于不超过阈值的小内存请求则继续采用线性分配的方式进行管理支持内存池扩容。二、设计2.1 底层数据结构内存池结构体当中设计三个字段max是内存池存放小模块的总数、head指向小模块链表的首节点large指向大模块的首节点current存放当前可用的小内存入口指针。//内存池structmp_pool_s{size_tmax;structmp_node_s*current;structmp_large_s*large;structmp_node_shead[0];};大结构体模块中next指向下一个节点alloc则是指向实际分配的大块内存区域的指针//大节点structmp_large_s{structmp_large_s*next;void*alloc;};小模块结构体mp_node_s中last是当前可分配位置的指针end是当前可分配位置结束的指针next指向下一个节点failed记录分配失败的次数。//小节点structmp_node_s{unsignedchar*last;unsignedchar*end;structmp_node_s*next;size_tfailed;};2.2 API内存池一共对外暴露六个接口2.2.1 内存池创建与销毁API创建内存池mp_create传递内存池大小size作为参数返回值是内存池实例的指针销毁内存池mp_destory传递内存池实例的指针为参数无返回值2.2.2 内存管理API分配指定长度内存mp_alloc并且对齐分配指定长度内存并且清零mp_calloc并且对齐分配指定长度内存mp_nalloc不对齐以上三个函数传递内存池实例的指针和所需内存块大小size为参数无返回值释放内存mp_free传递内存池实例指针和待删除的内存块的指针为了大块内存管理小块内存随内存池销毁而释放structmp_pool_s*mp_create_pool(size_tsize);voidmp_destory_pool(structmp_pool_s*pool);void*mp_alloc(structmp_pool_s*pool,size_tsize);void*mp_nalloc(structmp_pool_s*pool,size_tsize);void*mp_calloc(structmp_pool_s*pool,size_tsize);voidmp_free(structmp_pool_s*pool,void*p);三、实现3.1 内存池初始化与销毁在本实现中创建内存池时将池头、节点和数据区放在同一段内存当中便于管理。还需要考虑到对齐的问题调用posix_memalign函数进行32位的对齐structmp_pool_s*mp_create_pool(size_tsize){structmp_pool_s*p;intretposix_memalign((void**)p,MP_ALIGNMENT,sizesizeof(structmp_pool_s)sizeof(structmp_node_s));if(ret){returnNULL;}p-max(sizeMP_MAX_ALLOC_FROM_POOL)?size:MP_MAX_ALLOC_FROM_POOL;p-currentp-head;p-largeNULL;p-head-last(unsignedchar*)psizeof(structmp_pool_s)sizeof(structmp_node_s);p-head-endp-head-lastsize;p-head-failed0;returnp;}内存池的销毁需要注意销毁顺序先使用循环遍历node节点调用free释放大块内存alloc然后再销毁小块内存最后再销毁内存池本身因为顺序错乱会导致野指针的问题。3.2 内存管理辅助函数mp_alloc等函数中调用了关键的两个辅助函数mp_alloc_block、mp_alloc_large分别用于内存池扩容和大内存分配。mp_alloc_block采用尾插法当内存分配失败的时候 分配一个新节点大小和head节点的大小保持一致调用posix_memalign分配新节点所需内存并且保证节点起始地址32位对齐。这里还做了一个针对current指针的优化通过判断mp_node_s中的failed字段只增不减是否为0来跳过已满的节点从而优化遍历次数。staticvoid*mp_alloc_block(structmp_pool_s*pool,size_tsize){unsignedchar*m;structmp_node_s*hpool-head;size_tpsize(size_t)(h-end-(unsignedchar*)h);intretposix_memalign((void**)m,MP_ALIGNMENT,psize);if(ret)returnNULL;structmp_node_s*p,*new_node,*current;new_node(structmp_node_s*)m;new_node-endmpsize;new_node-nextNULL;new_node-failed0;msizeof(structmp_node_s);mmp_align_ptr(m,MP_ALIGNMENT);new_node-lastmsize;currentpool-current;for(pcurrent;p-next;pp-next){if(p-failed4){//currentp-next;}}p-nextnew_node;pool-currentcurrent?current:new_node;returnm;}mp_alloc_large实现比较简单针对大于阈值的内存分配请求直接调用malloc分配内存将新的大节点mp_large_s用头插法插入链表3.3 内存核心分配函数mp_alloc是比较有代表性的mp_calloc和mp_nalloc均在其基础上实现。所以只阐述mp_alloc的实现阈值分离pool-max 决定走哪条路径小内存从节点池中分配快速、批量大内存直接 malloc灵活、单独管理void*mp_alloc(structmp_pool_s*pool,size_tsize){unsignedchar*m;structmp_node_s*p;if(sizepool-max){ppool-current;do{mmp_align_ptr(p-last,MP_ALIGNMENT);if((size_t)(p-end-m)size){p-lastmsize;returnm;}pp-next;}while(p);returnmp_alloc_block(pool,size);}returnmp_alloc_large(pool,size);}四、项目适配kvs_base.c文件将本项目所使用的malloc和free等内存管理函数做了包装处理通过宏定义开启和关闭可以直接适配jemalloc、stdlib和专用的内存池使用以kvs_malloc为例如此当我们在kvs_hash.c等其他模块中使用内存分配时可以轻松改变所使用的内存管理组件。/* Memory allocation wrappers */void*kvs_malloc(size_tsize){if(g_mp_pool){void*ptrmp_alloc(g_mp_pool,size);#ifdefDEBUGprintf([DEBUG] malloc(%zu) %p\n,size,ptr);#endifreturnptr;}else{#ifENABLE_JEMALLOCreturnje_malloc(size);#elsereturnmalloc(size);#endif}}