51Testing软件测试网onW8P tI}P Y 在用C语言开发时,特别是在服务器端,内存的使用会成为系统性能的一个瓶颈,如频繁的分配和释放内存,会不断的增加系统的内存碎片,影响内核之
后分配内存的效率,这个时候一个比较可行的做法是采用内存池,先分配好比较多的内存,然后在这个已经分配的内存里使用内存,这样就不需要内核过多的参与内
存分配和释放的过程。51Testing软件测试网f.yMZ4Ih:S b0N
/WC9?*UZmb#UW0 内存池根据应用不同有多种实现的策略,如有些分配很大的内存,然后将内存分配成大小相等的块,并将每个块链接起来进行管理。
0g/K(I2of$@0
51Testing软件测试网!IqV$cO_ 下面对模型介绍的时候,为了简单,不加入用于调试的编写技巧和为之准备的结构,其实主要是省去间接调用,有时为了调试,会将文件及所在行以及主要的变量状态输出。51Testing软件测试网QX!l{n4C]:@
51Testing软件测试网O`(jw6n 一、内存池访问接口
$?%bpfl0
:L(_e/Xy%Ao3l0 创建大小为size的新的内存池。51Testing软件测试网
aO%f#DwOKR0v
-_Sn-y:KW0
e"B)_0rq?3LC0
pool_t _pool_new_heap(int size); |
51Testing软件测试网6m"H"v2PwRtDOm(u*q 从指定内存池中分配大小为size的内存空间,这些空间会在内存池释放时,被自动的释放。51Testing软件测试网[r.E#i
Qn~4cS
[UW!H[
`PR{ H'Z0
51Testing软件测试网2u
gv.oU U _5B&AF0j
void *pool_malloc(pool_t, int size); |
P,wI d!@6V{V H0 内存池的大小,返回内存池中所有内存块的大小总和51Testing软件测试网 Q3~uu'q/~^7k@|X6G
51Testing软件测试网|7d?,yr#}\d8S
]9qiXl4p[0
51Testing软件测试网 T3Hm-A?
D9P6s%\K 释放内存池,这会导致所有内存被释放,同时内存池本身也被释放
EN s|*k,v0
"Bc8Pn5f0
8z5rG.u[(XU$? h:p(i-R0
void pool_free(pool_t p); |
:M |8z!r[(l0 还有其它的一些接口,但这些是主要的接口。
+ucvVTkr&X0
MT7p?3r @yh[0 二、数据结构51Testing软件测试网"x?:r"BH
51Testing软件测试网+~K"T \C/p&Z
51Testing软件测试网z/i0gm3V
struct pheap H;s k5y,e0{ f4lU"|X0 void *block; mKn Ah$_(f/q~
`0 int size, used;51Testing软件测试网A%ZN[1hG }; |
51Testing软件测试网\yq-oKyM*r 该结构表示内存池中一个内存块的抽象表示,51Testing软件测试网Ze4Ufw
_3z6P2n%mD#Qk L?0 1、block 用于指向由malloc所分配的内存地址。51Testing软件测试网+pWkr^3K/I
+oKkdV:I
a&t#O"d&H0 2、size 表示block所指向地址的内存大小。51Testing软件测试网9\b,F5iX+Lw:j/]6j
51Testing软件测试网k!D3x-L2x(` 3、used 表示多少处于已经使用的状态。在分配内存时,这个域很重要,它表示内存块可以被分配的偏移值,也就是从used开始的内存都是可以被从内存池中分配出去的。
-S0br(hvUX6z0
51Testing软件测试网8H(nr7^1_z\-q
j'Zz!L#r0Xf0
struct pfree51Testing软件测试网%Dg~b0rP { 9M,n.`$A _&]0 pool_cleanup_t f; is+H(X
^d8?.E| }0 void *arg;51Testing软件测试网v-W:M8^.uP,S struct pheap *heap;51Testing软件测试网"p&DT9HWd!?%m struct pfree *next; `$uR9Q.giO9L0};51Testing软件测试网'e:c5r5hf'S(l typedef void (*pool_cleanup_t)(void *arg); |
n.q cC~6W
{~Y0}0 这个结构用于实现一个链表,将所有的内存块链接起来。每一个内存块,对映一个这个结构,也就是每个struct pheap结构,都有一个struct pfree结构将其封装起来,这个结构主要实现下面几个功能:
zg x2z/l3G.A"y
^P0
@1Y ^8N c-U?w/p0 1、实现内存块的链表,用struct pfree *next连接起来,这是一个单链表。
0Rm2T:V2T{t6_0
51Testing软件测试网g2KnTq!S 2、内存块释放的回调。注册在释放内存时,如果释放这个内存块,主要是通过pool_cleanup_t f和void *arg两个域来完成这个功能。
+C/xD
v7J2{*Uh0
!r!t9~Q3?
W^ ^9\0 3、pheap域用于指向需要被放入链表的内存块,就是前面的结构。
6I#yu(vAm8W0
_^#f%Yh%`$G0
51Testing软件测试网|:p'iRr
typedef struct pool_struct51Testing软件测试网Y N5{([N&G&P { :P2A&y{;e y
b3K0 int size; QYh
e#Z? b-g0 struct pfree *cleanup;51Testing软件测试网a.p%bQj:D!wG struct pfree *cleanup_tail; 5dQ4~
T!M;@-AH$p0 struct pheap *heap;51Testing软件测试网!t\Q3m$iw\ } _pool, *pool_t; |
4J*v0U-Zn'~a&~/O0 结构中的域代表如下:
eB6J]$dr4aM0
C7faf7gbr&k0 heap:指向内存池中最新申请的内存块,在每次申请内存块时,都会将其指向新的内存块。
/yc{;W P&I,Hp*Nx0
J)O ~8Q&E)D"e0 cleanup和cleanup_tail:指向链表的头和尾的指针。
xLOT~t'c0
51Testing软件测试网YQf0r~l#M/};j2Q \ size:表示内存池中内存的大小,包括所有的内存块。
#O[/N2AZ^:W4s0;_F\b@%g7\4j0 这个结构的主要功能如下:
F;o,ih1w NKdW0
51Testing软件测试网9J0v9LCl K5i 1、管理内存块。通过cleanup和clean_tail两个指针,因为内存块在内存池中是以单链表的形式组织的,这两个指针分别指向链表的头和尾指针。51Testing软件测试网'J3DA Jn;K
51Testing软件测试网;VFkj/T 2、内存池中可用的内存大小。通过size域来统计完成。51Testing软件测试网 c'iwjls*]m0L
F(MgAC-s+V Y0 3、获取最新的内存块指针。通过heap指针在每次分配内存块时重新赋值来实现。
/H9| l!A4b6N0
51Testing软件测试网9SD2W*W)|H%^'B7| 这个内存池的实现,主要是依靠上面三个数据结构来完成,其实估计已经知道的差不多了。下面再讨论一些基本的
e3NYI a'R0
51Testing软件测试网vmR_X 三、创建一个空的内存池51Testing软件测试网2rX6YZnt
(Bb8p7j/z)|u2k-T0 创建一个空的内存池比较简单,就是初始化一个内存池结构,也就是pool_t结构,并将其链表置为NULL,大小为0,不包含任何的内存块。
%E?$c+K8]M0W0
T^(BIQ^0
,e%YMGT(t{"}0
pool_t _pool_new() Yhi4QI.qg0{ 9Dc3c.[]F;K%IS&LO4J0 pool_t p; 7u-Orz^Y7n0 while((p = malloc(sizeof(_pool))) == NULL) sleep(1); ^rWSJ(C8U
I!q0 p->cleanup = NULL; /|&E
sE"g*S~+d0 p->heap = NULL; tj3C&Y&z0 p->size = 0;51Testing软件测试网.q Vl{&nf*K return p; r8JpU,e$L0} |
K'u?q2S
ghb0 四、创建指定大小的内存池51Testing软件测试网SYu
lq~4I8e1j$}
51Testing软件测试网
MB7Mu W 创建一个包含大小为size内存块的内存池,这个操作分两步,先创建一个空的内存池,然后再创建一个内存块,并将该内存块加入内存池中,也就是将内存块加到内存池的链表末尾,并重新设定指针。
G
b;Z OeS0
51Testing软件测试网.FB$X Z9M4d(X-o
51Testing软件测试网6|Q ~ {UX)O
pool_t _pool_new_heap(int size)51Testing软件测试网O'J-^ hSn { "i-i!M(C
X|0 pool_t p; [ ~k?2]1l0 p = _pool_new();51Testing软件测试网 o0tXD1xs p->heap = _pool_heap(p,size); [z8ep)E8L_0 return p;51Testing软件测试网:beI\n6t
F
vn-V } |
51Testing软件测试网m6{7W lI#oPL 第一步是先调用_pool_new创建一个空的内存池结构,这个前面已经说明了,操作很简单。看第二步是怎么完成的
L6@
zO?-po
_e0
51Testing软件测试网\~1NP]@
A
mijmL\0
(B;E"Gi KD+_P0static struct pheap *_pool_heap(pool_t p, int size)51Testing软件测试网E/V~R4o*NCD-stm {51Testing软件测试网0|#x0@RDFJ struct pheap *ret; 4H"h1\4l:Ej+Ph0 struct pfree *clean;51Testing软件测试网Qn]!c3My
51Testing软件测试网 XPPs"d
b while((ret = _pool__malloc(sizeof(struct pheap))) == NULL) sleep(1);51Testing软件测试网.|["Rkr^ while((ret->block = _pool__malloc(size)) == NULL) sleep(1); 'h\0{8^-?+SV.TY:sD0 ret->size = size;51Testing软件测试网9E'z1H%f^;?y+p8D7E p->size += size; ;j(qu:@Gj%f3IE
{1Xw0 ret->used = 0;51Testing软件测试网,H K6ZZ D$CMVf
51Testing软件测试网T.Y7t;O,z_#f:v3Fq clean = _pool_free(p, _pool_heap_free, (void *)ret);51Testing软件测试网ak&z
NmJY] clean->heap = ret; /* for future use in finding used mem for pstrdup */ w1er]anq0 _pool_cleanup_append(p, clean); -}X:@NvszPm0
51Testing软件测试网7j z3\X'Y return ret; EN#v sJ w1@._d0} %cvu6G1JF:u0 |
x@~3N
Uh2Q0 第二步包含下面一些操作:51Testing软件测试网9[6p!Q5p#{3L7Cd
51Testing软件测试网I^2K9Fm\^8S7Z 1、申请一个大小为size的内存块,也就是前方中介绍的pheap结构。
"uT6Dk cZ]0
t^-si+l
f3p$s5\.P,|2~0 2、把这个内存块的大小size,加到内存池上,就是那个p->size += size;
X^Nus3v^0
,{['Fm0_/kk ML0 3、将内存块关联到struct pfree结构。这会指示内存释放的方式,也把内存加入到链表元素上。51Testing软件测试网(J7PXiE
Mu:?
pE
r![I*{[rk0 4、将struct pfree结构关联到内存池,其实就是加到链表的末尾。
/f&UD7u'Q LrH'nf0
51Testing软件测试网/k%a%D*K;z6w 下面会讨论第3和第4部是怎么实现的。
+uDp1[ h0