C语言内存池使用模型-1

上一篇 / 下一篇  2012-10-08 10:18:22 / 个人分类:C语言

51Testing软件测试网onW8P t I}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.oUU _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
int pool_size(pool_t p);
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  还有其它的一些接口,但这些是主要的接口。

+uc vVTkr&X0

MT7p?3r@yh[0  二、数据结构51Testing软件测试网"x?:r"BH

51Testing软件测试网+~ K"T\C/p&Z

51Testing软件测试网z/i0g m3V

struct pheap
H;sk5y,e0{
f4l U"|X0    void *block;
mKnAh$_(f/q~ `0    int size, used;51Testing软件测试网A%ZN[1hG
};
51Testing软件测试网\yq-oK yM*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,F5i X+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结构将其封装起来,这个结构主要实现下面几个功能:

zgx2z/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(v Am8W0

_^#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

C7faf7gb r&k0  heap:指向内存池中最新申请的内存块,在每次申请内存块时,都会将其指向新的内存块。

/yc{;WP&I,Hp*Nx0

J)O~8Q&E)D"e0  cleanup和cleanup_tail:指向链表的头和尾的指针。

xLO T~t'c0 51Testing软件测试网YQf0r~l#M/};j2Q \

  size:表示内存池中内存的大小,包括所有的内存块。

#O[/N2AZ^:W4s0

;_F\b@%g7\4j0  这个结构的主要功能如下:

F;o,ih1wNKdW0 51Testing软件测试网9J0v9LClK5i

  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软件测试网2rX6Y Z nt

(Bb8p7j/z)|u2k-T0  创建一个空的内存池比较简单,就是初始化一个内存池结构,也就是pool_t结构,并将其链表置为NULL,大小为0,不包含任何的内存块。

%E?$c+K8]M0W0

T^(BIQ^0

,e%YMGT(t{"}0
pool_t _pool_new()
Y hi4Q I.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{&n f*K
    return p;
r8JpU,e$L0}

K'u?q2S ghb0  四、创建指定大小的内存池51Testing软件测试网SYu lq~4I8e1j$}

51Testing软件测试网 MB7MuW

  创建一个包含大小为size内存块的内存池,这个操作分两步,先创建一个空的内存池,然后再创建一个内存块,并将该内存块加入内存池中,也就是将内存块加到内存池的链表末尾,并重新设定指针。

G b;ZOeS0 51Testing软件测试网.FB$XZ9M4d(X-o

51Testing软件测试网6|Q ~ {UX)O

pool_t _pool_new_heap(int size)51Testing软件测试网O'J-^ hS n
{
"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{7WlI#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软件测试网a k&z NmJ Y]
    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#vsJ 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_/kkML0  3、将内存块关联到struct pfree结构。这会指示内存释放的方式,也把内存加入到链表元素上。51Testing软件测试网(J7PXiE Mu:?

pE r![I*{[rk0  4、将struct pfree结构关联到内存池,其实就是加到链表的末尾。

/f&U D7u'Q LrH'nf0 51Testing软件测试网/k%a%D*K;z6w

  下面会讨论第3和第4部是怎么实现的。

+uDp1[ h0

TAG:

 

评分:0

我来说两句

Open Toolbar