G@0i5q7L'U'tz?0 五、内存池管理内存机制(单链表管理结构)
5k(m ^-q'A M
Iq*t+F051Testing软件测试网*u,BKl Md"dJg 这里主要是讨论,从内存块申请开始,就是申请了一个pheap结构指向内存块,然后内存池以什么形式将内存块组织起来,这一个很重要的结构是struct pfree结构。51Testing软件测试网1Ec&xo'~}N
51Testing软件测试网9je-V1p_+@@S 看一下前面申请内存块之后,这时还只是一个pheap结构,没有和内存池关联起来,在前方我们看到,是通过下面代码进行关联的
Wa^8n4q051Testing软件测试网EM:{4@p'L3mB51Testing软件测试网cs}8S:V
clean = _pool_free(p, _pool_heap_free, (void *)ret);51Testing软件测试网#wE$?xYe[(F] clean->heap = ret; /* for future use in finding used mem for pstrdup */ RX%`]9m9zP[0 _pool_cleanup_append(p, clean); |
51Testing软件测试网!Ec%GKX]9N&Joh_ _pool_free为该内存块定义的一个结构进行初始化,如下调用51Testing软件测试网g*BWY]*hr
"^&]Y!F:t(ZX0
T)s GTn1q(IO051Testing软件测试网6cqlJUFM static struct pfree *_pool_free(pool_t p, pool_cleanup_t f, void *arg)51Testing软件测试网ShL0Q+Rp*qZk {51Testing软件测试网G8_3wUgO struct pfree *ret; 7d?,p(blS0 while((ret = malloc(sizeof(struct pfree))) == NULL) sleep(1); 5sP^R(V0 ret->f = f;51Testing软件测试网 `+Ocfaj$`9B ret->arg = arg;51Testing软件测试网L5^;s5x+U|@
Hi7x+? ret->next = NULL;51Testing软件测试网u e{a5oY 51Testing软件测试网lqS/G-[:A return ret;51Testing软件测试网5`ljL8\/m0x6I }51Testing软件测试网-w1S5N0qh"C!i |
51Testing软件测试网0N'~[&`3sb x*Hs%qW 这个函数只是定义了一个sturct pfree结构,基本上是用struct pheap这个结构对其进行初始化的,可以看出这个结构的arg和heap域都是指向struct pheap结构。这是很重要的一步,内存池主要是管理这个结构的。
#P$qp$GJ!^E3dmv0#H}9c
tv+j
s0 注意这里的pool_cleanup_t是一个函数指针,在我们这里,它是_pool_heap_free。用于指示如何释放这个内存块,实现很简单,如下:
eh&jE0A$i\laR0C8E5^t$tG0
;T(x4S+@C-?I7B0static void _pool_heap_free(void *arg) ?dT:B(Z4f3x*`0{51Testing软件测试网(]-u{2R5s9E{ struct pheap *h = (struct pheap *)arg; 8n(V%jV^@.n,Zn0 free(h->block);
`"^?["At8o0 free(h); G)j6k$ivAF7Lz[0} |
a Kb9cN4eK-^A0 这个释放函数就很简单了吧,下面继续我们话题。
&i^8V+f$["W051Testing软件测试网
w%f TRE&I\*Y 前文说了内存池包含链表,管理内存块,那接下来的操作是不是要将这个内存块【struct pfree】加到链表上。看一下_pool_cleanup_append函数做哪些工作:
Q+fO-p-u9w-Z0@Y*v.PdG)J6pM9^0
js2[krFz*`0|['b su!|0static void _pool_cleanup_append(pool_t p, struct pfree *pf) 7`HR|/}2Z8V1E3~0{51Testing软件测试网#\\Cdt_)@:C]^ struct pfree *cur; tCN;j:^}3W|051Testing软件测试网$}wj
u3H%s'z if(p->cleanup == NULL)51Testing软件测试网4k,P.|L_ { H|zAYw2u
R0 p->cleanup = pf; {u0}0Y;t.Ic0 p->cleanup_tail = pf;51Testing软件测试网X6aS&I%lS3]%w return; W)YW^ g#n rwq[^3H0 }51Testing软件测试网Vp]|l)UAA 51Testing软件测试网Qb
d
v c cur = p->cleanup_tail;51Testing软件测试网3Ys"N:Vt"~ cur->next = pf; 2Ee`6N @ _ x0 p->cleanup_tail = pf; cn/M%D$vW0}51Testing软件测试网+i-Q9Kj!B(kbH |
H.E$O6\?p%mdLb#Z0 这个函数很简单,将struct pfree结构加到内存池的cleanup_tail链表的末尾,并将新的cleanup_tail指向刚加入的pfree结构。
Q
eVp D2h|p!~051Testing软件测试网1s7ock7{(}iLX 到这里,就完成了内存从调用malloc分配至加入到内存池的过程,再回顾一下:
tZb3kCCyS|9^0L8~\4Q[ V };U#_0 1、调用malloc分配内存块,并赋值给struct pheap结构。
1wQ-x PoA3K[T051Testing软件测试网9u#J0r"eq4C;R,|2P9z,P 2、将struct pheap结构封装成struct pfree结构,这样struct pheap结构就可以成为链表上的元素。
nJ Z~%P.rM
wg%p0S7c,_.i%^'y9{y0 3、将struct pfree结构加入到struct pool_sturct结构【这是内存池的结构】的链表末尾。
o/^/D9A$[6C"? kn051Testing软件测试网#ut3DN D;EKOv 六、内存池的释放51Testing软件测试网"Tnj:M0_H7BI!n
!\*_
^9]Q!v$OR0 在内存池的使命结束后,我们需要释放内存池,不仅仅是struct pool_struct这个结构,还包括链表上的内存块。
9D5H;z/s+w-h:U0O4`N-~XJ/Ba0
#H @`m+kDC{l051Testing软件测试网2v1j^2mvV void pool_free(pool_t p)51Testing软件测试网Qpc9p%D {51Testing软件测试网cM@"I'Fm struct pfree *cur, *stub; l0Ok Y6u.Y)a,I0rCa4Y.WW0 if(p == NULL) return;51Testing软件测试网ud|m/~H N5S 51Testing软件测试网e'j$Q&Q)~hX[n cur = p->cleanup; W D}p};f^0 while(cur != NULL)51Testing软件测试网%T f%G)Hl*m R5? {51Testing软件测试网3X@\[c$q (*cur->f)(cur->arg); // 这会释放用malloc分配的内存块和struct pheap结构所占用的内存。这就是前文的_pool_heap_free函数51Testing软件测试网`{2l~TA'u stub = cur->next; $erhC"zq[j0 free(cur); // 释放pfree结构。 :TNRp+S6N#|w0 cur = stub; -]TX!Fq$X*W0 } GF:Sl4aHF5V0 free(p); //释放pool_struct结构所占用的内存。 xf
MLQ#sV0}51Testing软件测试网s!K)UdZ!E |
/j(m&u8oOD*Zw
w0 七、从内存池分配内存51Testing软件测试网:[ kC-mn7r q
,p+tq`T;h2a+o.e)n"PL0 这个才是本文的重点,如何从内存池中分配一个没有被使用的内存,先看代码,表示从内存池中分配size大小的内存,这个机制也是本文的重点。
'K^QL`9jS Z0V03k4^|%t:KH5d0
Tc:Y"C*m*~0"BEAV'IlI0void *pool_malloc(pool_t p, int size) $o"F YS3[P$w%u"R0{51Testing软件测试网b,qg$fBo-g
P2j.Cv@ void *block;51Testing软件测试网U$W-Z*Z:g6s[FJ 51Testing软件测试网Xf:P eH'L if(p == NULL) d^:iA:E,Xr-Ut0 {51Testing软件测试网-i1c m#Ju9|;se$d fprintf(stderr,"Memory Leak! [pmalloc received NULL pool, unable to track allocation, exiting]\n"); b'?"bfF2YC0 abort();51Testing软件测试网)Q,?\W}4JA&S5cR } nu_D,o6}#Q0T8\:t%iQ
d |0 if(p->heap == NULL || size > (p->heap->size / 2)) ?F s:u+{?{qTr0 {51Testing软件测试网(Nn"J2C"gD while((block = malloc(size)) == NULL) sleep(1);51Testing软件测试网q8]~,{[`%\
N p->size += size;51Testing软件测试网q5Ta]Ld[$E/Y,a._ _pool_cleanup_append(p, _pool_free(p, _pool__free, block));51Testing软件测试网,rXoB ^,Vx!cH,iS return block; VRS;b4h0 } x0OQ7QKv#SBP1R051Testing软件测试网-GV,HmU
eE%~sY if(size >= 4) a0fW`4b4r:W
wU0 while(p->heap->used&7) p->heap->used++; // 这一步是个对齐操作,如果已经使用的不是8的倍数,就会跳过,直到为8的倍数。 8llz@H+t%c0.AYr-^B
F
n0 if(size > (p->heap->size - p->heap->used)) *AcVM"q1Q6?Y0 p->heap = _pool_heap(p, p->heap->size); 8f#?w5L.tf7c0#Ty'ZLM#X
|0 block = (char *)p->heap->block + p->heap->used; $e0V
NJb@2dM
SN0 p->heap->used += size; //已经使用部分增加,也会使得内存分配的偏移值指针增加。51Testing软件测试网w7r4W"j0Nl return block;51Testing软件测试网 jK&@2h4M @}E^f } k
D@e/R-N0 |
51Testing软件测试网#V1?8f0y
W 从上面的代码中,可以看出分配内存的策略如下:51Testing软件测试网Va ~n.Z+M/y!\
'M'C8l5Z.c? ^,Bw0 1、如果内存池中没有内存或者将要分配的内存比内存池中总的内存数一半还要大,会调用malloc向系统请求内存。并把分配的内存加入到内存池中。
2\!km0z#OVX:]0{kp,peF0 2、已分配的内存按字对齐,如果没有对齐,就会跳过不对齐部分,不对齐的部分标识为已经使用。
4Y$Q$`e(E"iI2U051Testing软件测试网
Zw@!Sl3x&~e 3、如果链表最新的内存块内存剩余的大小不足于分配size字节,会重新请求新的内存块,并加入内存池。
FR
\C
W8ZL1pX051Testing软件测试网6g&v\M5q@@+Ly _ R 4、从链表的最后内存块分配,p->heap->used,表示内存块中已经使用的一个偏移,从这里开始分配。51Testing软件测试网[,PeN!|4B.}LE