内核中经常进行内存的分配和释放。为了便于数据的频繁分配和回收,通常建立一个空闲链表——内存池。当不使用的已分配的内存时,将其放入内存池中,而不是直接释放掉。

Linux内核提供了slab层来管理内存的分配和释放。频繁分配和回收必然导致内存碎片,slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,

从而避免了常见的碎片问题。

1.缓存

Linux 内核的缓存管理者有时称为" slab 分配器". 因此, 它的功能和类型在<linux/slab.h> 中声明. slab 分配器实现有一个 kmem_cache_t 类型的缓存; 使用一个
对 kmem_cache_create 的调用来创建它们:
kmem_cache_t *kmem_cache_create(const char *name, size_t size,
                size_t offset,
                unsigned long flags,
                void (*constructor)(void *, kmem_cache_t *, unsigned long flags),

                void (*destructor)(void *, kmem_cache_t *, unsignedlong flags));
这个函数创建一个新的可以驻留任意数目全部同样大小的内存区的缓存对象, 大小由size 参数指定. name 参数和这个缓存关联并且作为一个在追踪问题时有用的管理信息(在 /proc/slabinfo 中);通常, 它被设置为被缓存的结构类型的名子. 这个缓存保留一个指向 name 的指针, 而不是拷贝它, 因此驱动应当传递一个指向在静态存储中的名子的指针(常常这个名子只是一个文字字串). 这个名子不能包含空格.

offset 是页内的第一个对象的偏移; 它可被用来确保一个对被分配的对象的特殊对齐, 但是你最可能会使用 0 来请求缺省值.

flags 控制如何进行分配并且是下列标志的一个位掩码:
SLAB_NO_REAP
设置这个标志保护缓存在系统查找内存时被削减. 设置这个标志通常是个坏主意; 重要的是避免不必要地限制内存分配器的行动自由.
SLAB_HWCACHE_ALIGN
这个标志需要每个数据对象被对齐到一个缓存行; 实际对齐依赖主机平台的缓存分布. 这个选项可以是一个好的选择, 如果在 SMP 机器上你的缓存包含频繁存取的项. 但是, 用来获得缓存行对齐的填充可以浪费可观的内存量.
SLAB_CACHE_DMA
这个标志要求每个数据对象在 DMA 内存区分配.

函数的 constructor 和 destructor 参数是可选函数( 但是如果没有 constructor, 可能没有 destructor,); 前者可以用来初始化新分配的对象, 后者可以用来"清理"对象在它们的内存被作为一个整体释放回给系统之前.

你可以通过调用 kmem_cache_alloc 从缓存分配对象.
void *kmem_cache_alloc(kmem_cache_t *cache, int flags);
这里, cache 参数是你之前已经创建的缓存; flags 是你会传递给 kmalloc 的相同, 如果 kmem_cache_alloc 需要出去并分配更多内存(当缓存为空时)需要参考flags.
为释放一个对象, 使用 kmem_cache_free:
void kmem_cache_free(kmem_cache_t *cache, const void *obj);

当用完这个缓存, 典型地当模块被卸载, 它应当如下释放它的缓存:

int kmem_cache_destroy(kmem_cache_t *cache);

2.内存池

在内核中有不少地方内存分配不允许失败. 作为一个在这些情况下确保分配的方式, 内核开发者创建了一个已知为内存池(或者是 "mempool" )的抽象. 一个内存池真实地只是一类缓存, 它尽力一直保持一个空闲内存列表给紧急时使用.
一个内存池有一个类型 mempool_t ( 在 <linux/mempool.h> 中定义); 你可以使用mempool_create 创建一个:
mempool_t *mempool_create(int min_nr,mempool_alloc_t *alloc_fn, mempool_free_t *free_fn, void *pool_data);
min_nr 参数是内存池应当一直保留的最小数量的分配的对象. 实际的分配和释放对象由alloc_fn 和 free_fn 处理, 它们有这些原型:
typedef void *(mempool_alloc_t)(int gfp_mask, void *pool_data);
typedef void (mempool_free_t)(void *element, void *pool_data);
给 mempool_create 最后的参数 ( pool_data ) 被传递给 alloc_fn 和 free_fn. pool_data是内存源。内存池对象创建完成后会自动调用alloc方法从pool_data上分配min_nr个元素用来填充内存池。
如果需要, 你可编写特殊用途的函数来处理 mempool 的内存分配. 但是, 你常常只需要使内核 slab 分配器为你处理这个任务.

有 2 个函数 ( mempool_alloc_slab 和mempool_free_slab) 来进行内存分配.

因此, 设置内存池的代码常常看来如此:
cache = kmem_cache_create(. . .);
pool = mempool_create(MY_POOL_MINIMUM,mempool_alloc_slab, mempool_free_slab, cache);
一旦已创建了内存池, 可以分配和释放对象,使用:

void *mempool_alloc(mempool_t *pool, int gfp_mask);
void mempool_free(void *element, mempool_t *pool);
当内存池创建了, 分配函数将被调用足够的次数来创建一个预先分配的对象池. 因此, 对mempool_alloc 的调用试图从分配函数请求额外的对象; 如果那个分配失败, 一个预先分配的对象(如果有剩下的)被返回. 当一个对象被用 mempool_free 释放, 如果对齐预分配的对象数目小于最小量,它保留在池中; 否则, 它将被返回给系统.
一个 mempool 可被重新定大小, 使用:
int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask);
这个调用, 如果成功, 调整内存池的大小至少有 new_min_nr 个对象.

如果你不再需要一个内存池, 返回给系统使用:
void mempool_destroy(mempool_t *pool);
你编写返回所有的分配的对象, 在销毁 mempool 之前, 否则会产生一个内核 oops.
如果你考虑在你的驱动中使用一个 mempool, 请记住一件事: mempools 分配一块内存在一个链表中, 对任何真实的使用是空闲和无用的. 容易使用 mempools 消耗大量的内存.
在几乎每个情况下, 首选的可选项是不使用 mempool 并且代替以简单处理分配失败的可能性. 如果你的驱动有任何方法以不危害到系统完整性的方式来响应一个分配失败, 就这
样做. 驱动代码中的 mempools 的使用应当少.

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/slab.h>

#include <linux/errno.h>

#include <linux/mempool.h>

#include <linux/gfp.h>

#include <linux/delay.h>

struct my_mempoolobj{

int val;

};

#define MY_MIN_MEMPOOL 20

struct kmem_cache * mempool_cachep = NULL;

mempool_t * my_pool = NULL;

struct my_mempoolobj * mempool_object = NULL;

static int __init mempool_init(void){

mempool_cachep = kmem_cache_create("mempool_cachep", sizeof(struct my_mempool), 0, 0, NULL);

if(!mempool_cachep)

return -ENOMEM;

my_pool = mempool_create(MY_MIN_MEMPOOL, mempool_alloc_slab, mempool_free_slab, mempool_cachep);

if(!my_pool)

return -ENOMEM;

mempool_object = (struct my_mempoolobj *)mempool_alloc(my_pool, GFP_KERNEL);

if(mempool_object)

printk("one object has been allocated!\n");

else

goto fail;

printk("memory pool curr_nr is %d, min_nr is %d\n", my_pool->curr_nr, my_pool->min_nr);

mempool_free(mempool_object, my_pool);

return 0;

fail:

mempool_destroy(my_pool);

kmem_cache_destroy(mempool_cachep);

return -1;

}

static void __exit mempool_exit(void){

printk("memory pool test module exit!\n");

if(my_pool){

mempool_destroy(my_pool);

printk("mempool has been destroy!\n");

}

if(mempool_cachep){

kmem_cache_destroy(mempool_cachep);

printk("cache has been destroy!\n");

}

}

module_init(mempool_init);

module_exit(mempool_exit);

Linux 内核内存池的更多相关文章

  1. Linux内核内存管理算法Buddy和Slab: /proc/meminfo、/proc/buddyinfo、/proc/slabinfo

    slabtop cat /proc/slabinfo # name <active_objs> <num_objs> <objsize> <objpersla ...

  2. linux内核内存分配(三、虚拟内存管理)

    在分析虚拟内存管理前要先看下linux内核内存的具体分配我開始就是困在这个地方.对内核内存的分类不是非常清晰.我摘录当中的一段: 内核内存地址 ============================ ...

  3. Linux内核-内存回收逻辑和算法(LRU)

    Linux内核内存回收逻辑和算法(LRU) LRU 链表 在 Linux 中,操作系统对 LRU 的实现主要是基于一对双向链表:active 链表和 inactive 链表,这两个链表是 Linux ...

  4. LINUX内核内存屏障

    =================                          LINUX内核内存屏障                          ================= By ...

  5. Linux服务器内存池技术是如何实现的

    Linux服务器内存池技术是如何实现的

  6. Linux内核内存管理

    <Linux内核设计与实现>读书笔记(十二)- 内存管理   内核的内存使用不像用户空间那样随意,内核的内存出现错误时也只有靠自己来解决(用户空间的内存错误可以抛给内核来解决). 所有内核 ...

  7. Linux内核——内存管理

    内存管理 页 内核把物理页作为内存管理的基本单位.内存管理单元(MMU,管理内存并把虚拟地址转换为物理地址)通常以页为单位进行处理.MMU以页大小为单位来管理系统中的页表. 从虚拟内存的角度看,页就是 ...

  8. Linux内核内存管理子系统分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51298718 版权声明:本文为博主原创文章,未经博主允许不得转载. 还是那张熟悉 ...

  9. linux内核--内存管理(二)

    一.进程与内存     所有进程(执行的程序)都必须占用一定数量的内存,它或是用来存放从磁盘载入的程序代码,或是存放取自用户输入的数据等等.不过进程对这些内存的管理方式因内存用途不一而不尽相同,有些内 ...

随机推荐

  1. 机器学习作业(三)多类别分类与神经网络——Matlab实现

    题目太长了!下载地址[传送门] 第1题 简述:识别图片上的数字. 第1步:读取数据文件: %% Setup the parameters you will use for this part of t ...

  2. lampp ERROR 1044 (42000): Access denied for user ''@'localhost' to database 'lepus'

    解决方法: 在[mysqlld]段下增加如下代码:skip-grant-tables: 1.which mysql 查看mysql位置,例如:/opt/lampp/bin/mysql 2.进入配置my ...

  3. Sulley安装手记

    Sulley折腾手记 序言:sulley是有名的模糊测试架构,可是他的安装十分繁琐,容易出错,以致这么好的工具不被太多人关注,本人大一小白一枚,想入坑sulley,可是网上资料太少,且不连贯,费了九牛 ...

  4. S3C2440的时钟原理

    Crystal 无源晶体Oscillator 有源晶体(里面有有源器件) 无源晶振内只有一片按一定轴向切割的石英晶体薄片,供接入运放(或微处理器的XTAL端) 以形成振荡.有源晶振内带运放,工作在最佳 ...

  5. 初识消息队列--ActiveMq

    消息队列 即MessageQueue,是一种消息中间件,在遇到系统请求量比较大的情况下,导致请求堆积过多无法及时返回,可以通过它进行异步的消息处理,从而缓解系统压力. ActiveMq ActiveM ...

  6. 油候插件grant的使用

    // ==UserScript== // @name Test Baidu // @namespace http://www.baidu.com/ // @version 0.1 // @descri ...

  7. MyEclipse把普通的项目变成hibernate项目

  8. (转)KMP算法

    转自:http://blog.csdn.net/yutianzuijin/article/details/11954939 我们首先用一个图来描述kmp算法的思想.在字符串O中寻找f,当匹配到位置i时 ...

  9. 使用Vue-MUI轮播图失效问题解决案例(在Vue的update中执行)

    我使用的是mui+vue,社区关于轮播图失效的问题也有几个.我这边遇到的一个情况是我把所有的东西都写到plusReady事件中会导致轮播图搞死都不动,按照其他问答解决了vue生命周期等等的问题.提出来 ...

  10. Android 开发 微信分享,登陆,获取信息

    1 获取appid和appsecret.        https://open.weixin.qq.com/cgi-bin/index?t=home/index&lang=zh_CN     ...