文章链接:https://codemouse.online/archives/2020-06-26-23-50-33
内存池
在日常的写代码中,经常有需要申请内存的时候,但是频繁的申请释放会特别的浪费时间,于是衍生了内存池,由内存池帮忙管理内存,内存池帮忙统一释放,免去了用户的频繁释放,申请内存只需要从内存池中已经申请好的内存中取出,如果没有大于需求的内存,则内存池再去申请一块回来。
内存池增强了程序员对内存碎片话的管理,加快了内存的使用速度。
这个内存池的模型是参考了nginx内使用的内存池,相当于一个简化版,仅供参考。
本例子中,暂时没有写对小块内存的释放处理,只有在摧毁内存池的时候,才会去释放小块内存,这个还待优化。在大块内存中,用完就可以直接释放掉,不用加以保留。
小块内存其实可以根据使用的剩余大小做一个限制,超过一定界限,再通过设定一个flag,当没人用的时候就设定为0,这时候将内存初始化,清零一下,相当于空出一块内存,省去了申请的过程。有兴趣的可以实现一下。我说的flag也就是我小块内存的failed变量。
模型图
左边是小块内存的管理,右边是大块内存的管理。
实现代码
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define MP_ALIGNMENT 32
#define MP_PAGE_SIZE 4096
#define MP_MAX_ALLOC_FROM_POOL (MP_PAGE_SIZE-1)
#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))
struct mp_large_s
{
struct mp_large_s
*next
;
void *alloc
;
};
struct mp_node_s
{
unsigned char *last
;
unsigned char *end
;
struct mp_node_s
*next
;
size_t failed
;
};
struct mp_pool_s
{
size_t max
;
struct mp_node_s
*current
;
struct mp_large_s
*large
;
struct mp_node_s head
[0];
};
struct mp_pool_s
*mp_create_pool(size_t size
);
void mp_destory_pool(struct mp_pool_s
*pool
);
void *mp_alloc(struct mp_pool_s
*pool
, size_t size
);
void *mp_nalloc(struct mp_pool_s
*pool
, size_t size
);
void *mp_calloc(struct mp_pool_s
*pool
, size_t size
);
void mp_free(struct mp_pool_s
*pool
, void *p
);
struct mp_pool_s
*mp_create_pool(size_t size
) {
struct mp_pool_s
*p
;
int ret
= posix_memalign((void **)&p
, MP_ALIGNMENT
, size
+ sizeof(struct mp_pool_s
) + sizeof(struct mp_node_s
));
if (ret
) {
return NULL;
}
p
->max
= (size
< MP_MAX_ALLOC_FROM_POOL
) ? size
: MP_MAX_ALLOC_FROM_POOL
;
p
->current
= p
->head
;
p
->large
= NULL;
p
->head
->last
= (unsigned char *)p
+ sizeof(struct mp_pool_s
) + sizeof(struct mp_node_s
);
p
->head
->end
= p
->head
->last
+ size
;
p
->head
->failed
= 0;
return p
;
}
void mp_destory_pool(struct mp_pool_s
*pool
) {
struct mp_node_s
*h
, *n
;
struct mp_large_s
*l
;
for (l
= pool
->large
; l
; l
= l
->next
) {
if (l
->alloc
) {
free(l
->alloc
);
}
}
h
= pool
->head
->next
;
while (h
) {
n
= h
->next
;
free(h
);
h
= n
;
}
free(pool
);
}
void mp_reset_pool(struct mp_pool_s
*pool
) {
struct mp_node_s
*h
;
struct mp_large_s
*l
;
for (l
= pool
->large
; l
; l
= l
->next
) {
if (l
->alloc
) {
free(l
->alloc
);
}
}
pool
->large
= NULL;
for (h
= pool
->head
; h
; h
= h
->next
) {
h
->last
= (unsigned char *)h
+ sizeof(struct mp_node_s
);
}
}
static void *mp_alloc_block(struct mp_pool_s
*pool
, size_t size
) {
unsigned char *m
;
struct mp_node_s
*h
= pool
->head
;
size_t psize
= (size_t
)(h
->end
- (unsigned char *)h
);
int ret
= posix_memalign((void **)&m
, MP_ALIGNMENT
, psize
);
if (ret
) return NULL;
struct mp_node_s
*p
, *new_node
, *current
;
new_node
= (struct mp_node_s
*)m
;
new_node
->end
= m
+ psize
;
new_node
->next
= NULL;
new_node
->failed
= 0;
m
+= sizeof(struct mp_node_s
);
m
= mp_align_ptr(m
, MP_ALIGNMENT
);
new_node
->last
= m
+ size
;
current
= current
= pool
->current
;
new_node
->next
= current
;
pool
->current
= new_node
;
return m
;
}
static void *mp_alloc_large(struct mp_pool_s
*pool
, size_t size
) {
void *p
;
int ret
= posix_memalign(&p
, MP_ALIGNMENT
, size
);
if (ret
) {
return NULL;
}
size_t n
= 0;
struct mp_large_s
*large
;
for (large
= pool
->large
; large
; large
= large
->next
) {
if (large
->alloc
== NULL) {
large
->alloc
= p
;
return p
;
}
}
large
= mp_alloc(pool
, sizeof(struct mp_large_s
));
if (large
== NULL) {
free(p
);
return NULL;
}
large
->alloc
= p
;
large
->next
= pool
->large
;
pool
->large
= large
;
return p
;
}
void *mp_memalign(struct mp_pool_s
*pool
, size_t size
, size_t alignment
) {
void *p
;
int ret
= posix_memalign(&p
, alignment
, size
);
if (ret
) {
return NULL;
}
struct mp_large_s
*large
= mp_alloc(pool
, sizeof(struct mp_large_s
));
if (large
== NULL) {
free(p
);
return NULL;
}
large
->alloc
= p
;
large
->next
= pool
->large
;
pool
->large
= large
;
return p
;
}
void *mp_alloc(struct mp_pool_s
*pool
, size_t size
) {
unsigned char *m
;
struct mp_node_s
*p
;
if (size
<= pool
->max
) {
p
= pool
->current
;
do {
m
= mp_align_ptr(p
->last
, MP_ALIGNMENT
);
if ((size_t
)(p
->end
- m
) >= size
) {
p
->last
= m
+ size
;
return m
;
}
p
= p
->next
;
} while (p
);
return mp_alloc_block(pool
, size
);
}
return mp_alloc_large(pool
, size
);
}
void *mp_nalloc(struct mp_pool_s
*pool
, size_t size
) {
unsigned char *m
;
struct mp_node_s
*p
;
if (size
<= pool
->max
) {
p
= pool
->current
;
do {
m
= p
->last
;
if ((size_t
)(p
->end
- m
) >= size
) {
p
->last
= m
+size
;
return m
;
}
p
= p
->next
;
} while (p
);
return mp_alloc_block(pool
, size
);
}
return mp_alloc_large(pool
, size
);
}
void *mp_calloc(struct mp_pool_s
*pool
, size_t size
) {
void *p
= mp_alloc(pool
, size
);
if (p
) {
memset(p
, 0, size
);
}
return p
;
}
void mp_free(struct mp_pool_s
*pool
, void *p
) {
struct mp_large_s
*l
;
for (l
= pool
->large
; l
; l
= l
->next
) {
if (p
== l
->alloc
) {
free(l
->alloc
);
l
->alloc
= NULL;
return ;
}
}
}
使用范例
int main(int argc
, char *argv
[]) {
int size
= 1 << 12;
struct mp_pool_s
*p
= mp_create_pool(size
);
int i
= 0;
for (i
= 0;i
< 10;i
++) {
void *mp
= mp_alloc(p
, 512);
}
printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n", mp_align(24, 32), mp_align(17, 32));
int j
= 0;
for (i
= 0;i
< 5;i
++) {
char *pp
= mp_calloc(p
, 32);
for (j
= 0;j
< 32;j
++) {
if (pp
[j
]) {
printf("calloc wrong\n");
}
printf("calloc success\n");
}
}
for (i
= 0;i
< 5;i
++) {
void *l
= mp_alloc(p
, 8192);
mp_free(p
, l
);
}
mp_reset_pool(p
);
for (i
= 0;i
< 58;i
++) {
mp_alloc(p
, 256);
}
mp_destory_pool(p
);
return 0;
}