以前的文章中,码哥介绍过利用内存池有哪些优点,我们列举如下:
因此,本文不再赘述上面的部分。
这篇文章我们介绍一种级联结构内存池,该内存池的实现可以参考:Github - Water-Melon/Melon
池结构的模型大致如下:
------------- | 父池 | ------------- | | ----------- --------- | 子池1 | | 子池2 | ----------- --------- | | .... -------- -------- | 孙池1 | | 孙池2 | -------- --------这种结构是什么意思呢?
子池所使用的内存及其所能分配的内存均来自于父池。故此,孙池的内存也是由其依赖的子池而来的。
在Melon库的内存池组件中,内存的来源有三处:
我们先来看一个简单的内存池使用的例子:
#include #include #include "mln_core.h"#include "mln_log.h"#include "mln_alloc.h"int main(int argc, char *argv[]){ char *p; mln_alloc_t *pool; struct mln_core_attr cattr; /* libmelon init begin*/ cattr.argc = argc; cattr.argv = argv; cattr.global_init = NULL; cattr.master_process = NULL; cattr.worker_process = NULL; if (mln_core_init(&cattr) < 0) { fprintf(stderr, "init failed
"); return -1; } /* libmelon init end */ pool = mln_alloc_init(NULL); if (pool == NULL) { mln_log(error, "pool init failed
"); return -1; } p = (char *)mln_alloc_m(pool, 6); if (p == NULL) { mln_log(error, "alloc failed
"); return -1; } memcpy(p, "hello", 5); p[5] = 0; mln_log(debug, "%s
", p); mln_alloc_destroy(pool); return 0;} 在这个例子中,我们创建了一个堆内存池,并且利用该内存池分配了6个字节的内存区用于写入"hello"字符串。
这个例子很常规,与很多常见开源软件中的用法类似(例如nginx)。
下面看一个级联使用的例子:
#include #include #include "mln_core.h"#include "mln_log.h"#include "mln_alloc.h"int main(int argc, char *argv[]){ char *p; mln_alloc_t *pool, *parent; struct mln_core_attr cattr; /* libmelon init begin*/ cattr.argc = argc; cattr.argv = argv; cattr.global_init = NULL; cattr.master_process = NULL; cattr.worker_process = NULL; if (mln_core_init(&cattr) < 0) { fprintf(stderr, "init failed
"); return -1; } /* libmelon init end */ parent = mln_alloc_init(NULL); if (parent == NULL) { mln_log(error, "parent pool init failed
"); return -1; } pool = mln_alloc_init(parent); if (pool == NULL) { mln_log(error, "pool init failed
"); return -1; } p = (char *)mln_alloc_m(pool, 6); if (p == NULL) { mln_log(error, "alloc failed
"); return -1; } memcpy(p, "hello", 5); p[5] = 0; mln_log(debug, "%s
", p); mln_alloc_destroy(parent); return 0;} 可以看到,我们先从堆内存上创建了一个内存池名为parent,然后将其作为内存池pool的上层父池。在pool池创建后,我们从pool中分配一个6字节内存区,并写入hello字符串。
此时,内存区实际上是由父池parent分配而来,并在子池pool中被管理使用。换言之,这块内存区既被子池pool管理,也被父池parent管理。
最后,我们直接将父池parent进行了销毁,那么连带子池pool也就一同销毁了。
到这里,可能有的读者会问,这么多此一举的意义是什么?
我们可以通过下面一个例子来寻找答案:
#include #include #include "mln_core.h"#include "mln_log.h"#include "mln_alloc.h"#include "mln_defs.h"int func_lock(void *locker){ printf("lock
"); MLN_LOCK((mln_lock_t *)locker); return 0;}int func_unlock(void *locker){ printf("unlock
"); MLN_UNLOCK((mln_lock_t *)locker); return 0;}int main(int argc, char *argv[]){ char *p; mln_lock_t lock; mln_alloc_t *pool, *parent; struct mln_core_attr cattr; struct mln_alloc_shm_attr_s sattr; /* libmelon init begin*/ cattr.argc = argc; cattr.argv = argv; cattr.global_init = NULL; cattr.master_process = NULL; cattr.worker_process = NULL; if (mln_core_init(&cattr) < 0) { fprintf(stderr, "init failed
"); return -1; } /* libmelon init begin*/ /* create a shared memory pool*/ MLN_LOCK_INIT(&lock); sattr.size = 10 * 1024 * 1024; sattr.locker = &lock; sattr.lock = func_lock; sattr.unlock = func_unlock; parent = mln_alloc_shm_init(&sattr); if (parent == NULL) { mln_log(error, "parent pool init failed
"); return -1; } pool = mln_alloc_init(parent); if (pool == NULL) { mln_log(error, "pool init failed
"); return -1; } p = (char *)mln_alloc_m(pool, 6); if (p == NULL) { mln_log(error, "alloc failed
"); return -1; } memcpy(p, "hello", 5); p[5] = 0; mln_log(debug, "%s
", p); mln_alloc_destroy(parent); return 0;} 读者可以对比示例二和示例三的差异。
在这个例子中,我们将parent初始化成一个基于共享内存的内存池。因为涉及进程间资源争抢,因此需要给出内存池所使用的锁资源及其操作原语(即加解锁回调)。此处额外说一句,Melon的共享内存使用的锁是由使用者自行定义的,而不是强制配备互斥量或者读写锁之类的。
随后,由子池pool分配了一个6字节内存区,这个内存区实际上是由共享内存中而来。
到此,不知道读者是否明白级联内存池的一部分用意呢?
即:如果我们的整个程序的动态内存分配完全依赖于内存池的分配的话,那么只需要简单地将父池改为基于共享内存的内存池,就可以完成程序从堆到共享内存的迁移了。
事实上,在Melon中,使用级联结构操作共享内存有如下好处:
最后一个问题,我们将内存全部迁移到共享内存的意义是什么呢?
进程间零拷贝IPC
当一个进程A中维护的信息需要与另一个进程B进行交换和共享的时候,我们只需要将这些信息由A进程写入共享内存中一次,B进程就可以直接访问。而不需要将数据从A的地址空间拷贝到内核缓冲区,再由内和缓冲区拷贝到进程B的用户态缓冲区,这样的频繁复制。
甚至,如果进程A和B的可执行程序在同一CPU、操作系统、编译器版本和头文件下编译生成,那么我们甚至不需要对传递的消息做任何序列化就可以直接访问。
感谢阅读!
| 留言与评论(共有 0 条评论) “” |