00 squid 内存池研究

1. 概述

本文的squid原理及代码基于squid 4.12版的源码,主要阐述及分析了squid的内存池原理及响应的代码实现。
squid的内存池有两种模式,一种是poolmalloc模式,另外一种是poolchunked模式

2. 原理分析

从总体上来看,squid的内存池是通过按照不同大小的固定尺寸内存链来处理的。应用向内存池申请内存时,先找到应用需要的内存对应的内存链,再从内存链中获取一个可用的固定尺寸的内存块即可。

当内存链中的没有任何可用对象时,根据新申请的可用对象的申请方式,将内存池模式分为了malloc和chunked两种模式。

2.1 malloc 模式分析

此模式的内存池原理比较简单,每次应用向内存池申请内存时,先从内存池已有的空闲对象列表中直接pop出一个即可,如果空闲列表对象池中没有任何可用空闲对象,则直接调用系统的malloc进行申请;当应用归还内存时直接将归还的内存放入空闲队列中即可。

2.2 chunked 模式分析

此模式主要原理是,当内存链中没有可用空闲内存对象时,先向操作系统申请一块大内存(如2MB),再将这块大内存按照内存链对象的大小进行切分,将规整切分后的内存加入到可用内存链中。

3. 源码分析

内存源码位于src/mem目录下,

3.1 文件及目录结构说明

文件名 说明
AllocatorProxy.h和AllocatorProxy.cpp 提供给外部期望使用内存池分配类对象空间的代理类
forward.h 内存池对外头文件
Meter.h 跟踪内存使用情况的对象
old_api.cc 实现forward.h中的相关全局函数声明
pool.h 及pool.cpp 内存池对象接口
PoolMalloc.h及PoolMalloc.cc malloc内存池模式实现
PoolChunked.h及PoolChunked.cc chunked内存池模式实现

3.2 总体调用关系说明

程序启动的时候调用Mem::Init() 函数对全局的内存池进行初始化,默认全局初始化了2KB、4KB、8KB、16KB、32KB、64KB、dread_ctrl、dwrite_q、MD5 digest(16字节) 的全局内存池对象(static MemAllocator *pools[MEM_MAX]) ,并设置内存池对象类型的最大个数为mem_type::MEM_MAX个;

其中除了MD5 digest内存池的chunk块大小512字节外,其他内存池的chunk块大小均为2MB

针对String 类型,squid定义了一组特殊的小内存内存块。通过memAllocString 函数调用,默认定义了6中类型的内存池,大小分别是36Byte, 128Byte, 512Byte, 1024Byte, 4096Byte, 16KB 等6种,存放于static const PoolMeta PoolAttrs[mem_str_pool_count] 中

当程序申请内存时通过2中方式来申请,

  1. 调用memAllocBuf 来申请内存自定义大小的内存,当申请的内存超过pools中最大内存池64KB时则直接调用malloc进行申请
  2. 调用memAllocate根据类型MEM_NONE~~MEM_MAX的类型来调用指定大小的内存

3.3 PoolChunked源码分析

每个固定大小的内存池 MemPoolChunked 由多个MemChunk组成,MemPoolChunked 成员变量解释如下:

变量名 变量含义
chunk_size 当前内存池每个chunk的大小
chunk_capacity 每个chunk内可分配的内存item个数
chunkCount 当前内存池的chunk个数
freeCache 当前内存池中空闲item列表(自身是空闲的,自身前sizeof(void*)个字节内记录了下一个空闲对象的地址)
nextFreeChunk 当有新申请的空闲chunk时指向新申请的空闲chunk
Chunks 当前内存池chunk块链表的第一个chunk
allChunks 所有chunk块列表,使用伸展树(Splay Tree)存储

MemChunk 成员变量说明如下:

变量名 变量含义
freeList 当前chunk内空闲item列表
objCache 当前chunk的首个item地址
inuse_count 当前chunk中正在被使用的item个数
nextFreeChunk 当前chunk的前一个被使用完的空闲chunk
next 当前chunk的下一个chunk地址
lastref 最后使用时间
pool 所属的内存池对象地址