20150222 IMX257 Linux内存空间内存分配

2015-02-22     李海沿

不知道为什么,最近做梦总是梦见以前的事,以前的场景,可能是28号回学校的缘故吧!好了,不扯废话了,前面我针对gpio按键这个实验学习了中断,信号量,定时器等内核实现,下面我们,使用以前的字符设备模板来写一个Linux内存空间内存分配的实验。

一、KMALLOC

kmalloc 是一个功能强大且高速(除非被阻塞)的工具,所分配到的内存在物理内存中连续且保持原有的数据(不清零)。原型:


#include <linux/slab.h>

void *kmalloc(size_t size, int flags);

参数详解:

size 参数

内核管理系统的物理内存,物理内存只能按页面进行分配。kmalloc 和典型的用户空间 malloc 在实际上有很大的差别,内核使用特殊的基于页的分配技术,以最佳的方式利用系统 RAM。Linux 处理内存分配的方法:创建一系列内存对象集合,每个集合内的内存块大小是固定。处理分配请求时,就直接在包含有足够大内存块的集合中传递一个整块给请求 者。

必须注意的是:内核只能分配一些预定义的、固定大小的字节数组。kmalloc 能够处理的最小内存块是 32 或 64 字节(体系结构依赖),而内存块大小的上限随着体系和内核配置而变化。考虑到移植性,不应分配大于 128 KB的内存。若需多于几个 KB的内存块,最好使用其他方法。

flags 参数

内存分配最终总是调用 __get_free_pages 来进行实际的分配,这就是 GFP_ 前缀的由来。

所有标志都定义在 <linux/gfp.h> ,有符号代表常常使用的标志组合。

主要的标志常被称为分配优先级,包括:

GFP_KERNEL

最常用的标志,意思是这个分配代表运行在内核空间的进程进行。内核正常分配内存。当空闲内存较少时,可能进入休眠来等待一个页面。当前进程休眠时, 内核会采取适当的动作来获取空闲页。所以使用 GFP_KERNEL 来分配内存的函数必须是可重入,且不能在原子上下文中运行。

GFP_ATOMIC

内核通常会为原子性的分配预留一些空闲页。当当前进程不能被置为睡眠时,应使用 GFP_ATOMIC,这样kmalloc 甚至能够使用最后一个空闲页。如果连这最后一个空闲页也不存在,则分配返回失败。常用来从中断处理和进程上下文之外的其他代码中分配内存,从不睡眠。

GFP_USER

用来为用户空间分配内存页,可能睡眠。

GFP_HIGHUSER

类似 GFP_USER,如果有高端内存,就从高端内存分配。

GFP_NOIO

GFP_NOFS

功能类似 GFP_KERNEL,但是为内核分配内存的工作增加了限制。具有GFP_NOFS 的分配不允许执行任何文件系统调用,而 GFP_NOIO 禁止任何 I/O 初始化。它们主要用在文件系统和虚拟内存代码。那里允许分配休眠,但不应发生递归的文件系统调。


Linux 内核把内存分为 3 个区段: 可用于DMA的内存(位于一个特别的地址范围的内存, 外设可以在这里进行 DMA 存取)、常规内存和高端内存(为了访问(相对)大量的内存而存在的一种机制)。目的是使每中计算机平台都必须知道如何将自己特定的内存范围归类到这三个区 段中,而不是所有RAM都一样。

当要分配一个满足kmalloc要求的新页时, 内核会建立一个内存区段的列表以供搜索。若指定了 __GFP_DMA, 只有可用于DMA的内存区段被搜索;若没有指定特别的标志, 常规和 可用于DMA的内存区段都被搜索; 若 设置了 __GFP_HIGHMEM,所有的 3 个区段都被搜索(注意:kmalloc 不能分配高端内存)。

内存区段背后的机制在 mm/page_alloc.c 中实现, 且区段的初始化时平台相关的, 通常在 arch 目录树的 mm/init.c中。

有的标志用双下划线做前缀,他们可与上面标志"或"起来使用,以控制分配方式:

__GFP_DMA

要求分配可用于DMA的内存。

__GFP_HIGHMEM

分配的内存可以位于高端内存.

__GFP_COLD

通常,分配器试图返回"缓存热(cache warm)"页面(可在处理器缓存中找到的页面)。 而这个标志请求一个尚未使用的"冷"页面。对于用作 DMA 读取的页面分配,可使用此标志。因为此时页面在处理器缓存中没多大帮助。

__GFP_NOWARN

当一个分配无法满足,阻止内核发出警告(使用 printk )。

__GFP_HIGH

高优先级请求,允许为紧急状况消耗被内核保留的最后一些内存页。

__GFP_REPEAT

__GFP_NOFAIL

__GFP_NORETRY

告诉分配器当满足一个分配有困难时,如何动作。__GFP_REPEAT 表示努力再尝试一次,仍然可能失败; __GFP_NOFAIL 告诉分配器尽最大努力来满足要求,始终不返回失败,不推荐使用; __GFP_NORETRY 告知分配器如果无法满足请求,立即返回。

前面知识点摘自: http://www.douban.com/note/56607778/

二、后备高速缓存 (lookaside cache)

内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间,因此不应该将内存释放回一个全局的内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个互斥锁,那么只需在为互斥锁首次分配内存时执行一次互斥锁初始化函数(mutex_init)即可。后续的内存分配不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。

linux2.6中USB和SCSI驱动程序使用了这种高速缓存,是为一些反复使用的块增加某些特殊的内存池。后背高速缓存管理也叫slab分配器,相关函数和类型在<linux/slab.h>中申明。

slab分配器实现高速缓存具有kmem_cache_t类型。


kmem_cache_t * kmem_cache_create( const char *name, size_t size, size_t align,

unsigned long flags;

void (*constructor)(void*, kmem_cache_t *, unsigned long),

void (*destructor)(void*, kmem_cache_t *, unsigned long));

用于创建一个新的高速缓存对象。

constructor用于初始化新分配的对象,destructor用于清除对象。

一旦某个对象的高速缓存被创建以后,就可以调用kmem_cache_alloc从中分配内存对象。

void * kmem_cache_alloc(kmem_cache_t *cache,int flags);

释放内存对象使用kmem_cache_free

void kmem_cache_free(kmem_cache_t *cache,const void *obj);

在内存空间都被释放后,模块被卸载前,驱动程序应当释放他的高速缓存。

int kmem_cache_destory(kmem_cache_t *cache);

要检查其返回状态,如果失败,表明莫块中发生了内存泄露。

基于slab的高速缓存scullc


kmem_cache_t *scullc_cache;

scullc_cache=kmem_cache_creat("scullc",scullc_quantum,0,SLAB_HWCACHE_ALIGN,NULL,NULL);

if(!scullc_cache)

{

scullc_cleanup();

return -ENOMEM;

}

if(!dpte->data[s_pos])

{

dptr->data[s_pos]=kmem_cache_alloc(scullc_cache,GFP_KERNEL);

if(!dptr->data[s_pos])

goto nomem;

memset(dptr->data[s_pos],0,scullc_quantum);

}

for(i=0;i<qset;i++)

{

if(dptr->data[i])

kmem_cache_free(scullc_cache,dptr->data[i]);

}

if(scullc_cache)

kmem_cache_destory(scullc_cache);

三、内存池

内核中有些地方的内存分配是不允许失败的,为确保能分配成功,内核建立一种称为内存池的抽象,他试图始终保持空闲状态,以便紧急情况使用。


mempool_t * mempool_creat(int min_nr,

mempool_alloc_t *alloc_fn, //对象分分配 mempool_alloc_slab

mempool_free_t *free_fn, //释放 mempool_free_slab

void *pool_data);

可以用如下代码来构造内存池


cache=kmem_cache_creat(...); //创建一个高速缓存

pool=mempool_creat(MY_POOL_MINIMUM,mempool_alloc_slab,mempool_free_slab,cache);//建立内存池对象

void *mempool_alloc(mempool_t *poll,int gfp_mask);//分配对象

void *mempool_free(void *element,mempool_t *poll);//释放对象

void mempool_destroy(mempool_t *poll);//销毁内存池

注意:mempool会分配一些内存块,空闲且不会被用到,造成内存的大量浪费。所以一般情况不要用内存池。

四、实例分析

1.定义内存操作的指针

2.在init函数中,分别使用kmalloc,get_zeroed_page,vmalloc申请物理内存,整页内存,虚拟内存

3.申请内存之后就是读写内存

4.当我们不用内存时,在exit函数中释放内存,防止造成内存泄露

5.编译测试

如图所示,当我们insmod时,打印出了申请的内存的地址,并且成功的读出了我们写入的数据

附上驱动代码:

  1 /*在内存中申请1k 大小的内存做为简单的一个设备来访问*/
  2 #include<linux/cdev.h>
  3 #include<linux/module.h>
  4 #include<linux/types.h>
  5 #include<linux/fs.h>
  6 #include<linux/errno.h>
  7 #include<linux/mm.h>
  8 #include<linux/sched.h>
  9 #include<linux/init.h>
 10 #include<asm/io.h>
 11 #include<asm/system.h>
 12 #include<asm/uaccess.h>
 13 #include<linux/device.h>
 14 #include <linux/vmalloc.h>
 15
 16 #define Driver_NAME "kmalloc"
 17 #define DEVICE_NAME "kmalloc"
 18
 19 static int major = 0;
 20
 21 //auto to create device node
 22 static struct class *drv_class = NULL;
 23 static struct class_device *drv_class_dev = NULL;
 24
 25 //定义内存分配的指针
 26 char *buf1 = NULL;
 27 char *buf2 = NULL;
 28 char *buf3 = NULL;
 29
 30 static int key_open(struct inode *inode, struct file *file)
 31 {
 32     printk("<0>function open!\n\n");
 33     return 0;
 34 }
 35
 36 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
 37 {
 38     return 0;
 39 }
 40
 41 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
 42 {
 43     printk("<0>function write!\n\n");
 44     return 1;
 45 }
 46
 47 static int  key_release(struct inode *inode, struct file *filp)
 48 {
 49     printk("<0>function write!\n\n");
 50     return 0;
 51 }
 52
 53 static int key_ioctl(struct inode *inode,struct file *flip,unsigned int command,unsigned long arg)
 54 {
 55     printk("<0>function ioctl!\n\n");
 56     return 0;
 57 }
 58
 59 static struct file_operations key_fops = {
 60     .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
 61     .open   =   key_open,
 62     .read   =   key_read,
 63     .write  =   key_write,
 64     .release=   key_release,
 65     .ioctl  =   key_ioctl,
 66 };
 67
 68
 69 static int __init  key_irq_init(void)
 70 {
 71     printk("<0>\nHello,this is %s module!\n\n",Driver_NAME);
 72     //register and mknod
 73     major = register_chrdev(0,Driver_NAME,&key_fops);
 74     drv_class = class_create(THIS_MODULE,Driver_NAME);
 75     drv_class_dev = device_create(drv_class,NULL,MKDEV(major,0),NULL,DEVICE_NAME);  /*/dev/key_query*/
 76
 77     //kmalloc分配内存实验1
 78     // buf1 申请一个100字节的物理内存,若无内存,则休眠
 79     buf1 = (unsigned char *)kmalloc(100,GFP_KERNEL);
 80     if(buf1 == NULL){
 81         printk("<0>buf1 kmalloc error !\n\n");
 82         return -1;
 83     }
 84     printk("<0>buf1 kmalloc mem addr = %x \n\n",buf1);
 85     memset(buf1,0,100);                                //将所分配到的内存清理
 86     strcpy(buf1,"<<------buf1 Kmalloc Mem OK!------>>");//向内存中写入数据
 87     printk("<0>BUF1: %s\n\n",buf1);                    //从内存中读出数据
 88
 89     //get_zeroed_page申请分配整页内存,并且初始化为0
 90     buf2 = (unsigned char *)get_zeroed_page(GFP_KERNEL);
 91     if(buf2 == NULL){
 92         printk("<0>buf2 get_zeroed_page error !\n\n");
 93         return -1;
 94     }
 95     printk("<0>buf2 get_zeroed_page addr = %x \n\n",buf2);
 96     strcpy(buf2,"<<------buf2 get_zeroed_page OK!------>>");//向内存中写入数据
 97     printk("<0>BUF2: %s\n\n",buf2);
 98
 99     //Vmalloc 申请1000000个字节(1M)的空间 虚拟地址分配(零碎的物理地址空间)
100     buf3 = (unsigned char *)vmalloc(1000000);
101     if(buf3 == NULL){
102         printk("<0>buf3 vmalloc error !\n\n");
103         return -1;
104     }
105     printk("<0>buf3 vmalloc addr = %x \n\n",buf3);
106     strcpy(buf3,"<<------buf3 vmalloc OK!------>>");//向内存中写入数据
107     printk("<0>BUF3: %s\n\n",buf3);
108
109     return 0;
110 }
111
112 static void __exit key_irq_exit(void)
113 {
114     printk("<0>\nGoodbye,%s!\n\n",Driver_NAME);
115
116        unregister_chrdev(major,Driver_NAME);
117     device_unregister(drv_class_dev);
118     class_destroy(drv_class);
119
120     //释放前面申请的内存
121     printk("<0>buf1 kfree addr = %x \n\n",buf1);
122     kfree(buf1);
123     printk("<0>buf2 free_page addr = %x \n\n",buf2);
124     free_page((unsigned long)buf2);
125     printk("<0>buf3 vfree addr = %x \n\n",buf3);
126     vfree(buf3);
127     printk("<0><<------Module Exit!------>>\n\n");
128
129 }
130
131
132 /* 这两行指定驱动程序的初始化函数和卸载函数 */
133 module_init(key_irq_init);
134 module_exit(key_irq_exit);
135
136 /* 描述驱动程序的一些信息,不是必须的 */
137 MODULE_AUTHOR("Lover雪儿");
138 MODULE_VERSION("0.1.0");
139 MODULE_DESCRIPTION("IMX257 key Driver");
140 MODULE_LICENSE("GPL");

时间: 2024-10-13 00:50:26

20150222 IMX257 Linux内存空间内存分配的相关文章

Linux内核空间内存申请函数kmalloc、kzalloc、vmalloc的区别【转】

转自:http://www.th7.cn/system/lin/201606/167750.shtml 我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一致的,对应的用户空间内存释放函数是 free().注意:动态申请的内存使用完后必须要释放,否则会造成内存泄漏,如果内存泄漏发生在内核空间,则会造成系统崩溃. 那么,在内核空间中如何申请内存呢?一般我们会用到 kmalloc().kzalloc().vmalloc() 等,下面我们介绍一下这些函数的使

Java中内存空间的分配及回收

Java中内存分为: 栈:存放简单数据类型变量(值和变量名都存在栈中),存放引用数据类型的变量名以及它所指向的实例的首地址. 堆:存放引用数据类型的实例. Java的垃圾回收: 由一个后台线程GC(Garbage Collection)进行垃圾回收,虚拟机判定内存不够的时候会中断代码的运行,这个时候GC才进行垃圾回收. 缺点:不能够精确的去回收内存. java.lang.System.gc(); 上面代码会建议系统回收内存,但系统不一定回应,会先去看内存是否够用,够用则不予理睬,不够用才会去进行

Linux与jvm内存关系分析

原文出处: 美团技术团队 引言 在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约600m,Linux自身使用大约800m.从表面上,物理内存应该是足够使用的:但实际运行的情况是,会发生大量使用SWAP(说明物理内存不够使用了),如下图所示.同时,由于SWAP和GC同时发生会致使JVM严重卡顿,所以我们要追问:内存究竟去哪儿了? 要分析这个问题,理解JVM和操作系统之间的内存关系非常重要.接下来主要就Linux与

计算机操作系统学习笔记_7_内存管理 --内存管理基础

h2.western { font-family: "Liberation Sans",sans-serif; font-size: 16pt; }h2.cjk { font-family: "微软雅黑"; font-size: 16pt; }h2.ctl { font-family: "AR PL UMing CN"; font-size: 16pt; }h1 { margin-bottom: 0.21cm; }h1.western { fon

【操作系统】实验四 主存空间的分配和回收

1. 实验目的 用高级语言完成一个主存空间的分配和回收程序,以加深对动态分区分配方式及其算法的理解. 2.实验要求 采用连续分配方式之动态分区分配存储管理,使用首次适应算法.循环首次适应算法.最佳适应算法和最坏适应算法4种算法完成设计(任选两种算法). (1)设计一个作业申请队列以及作业完成后的释放顺序,实现主存的分配和回收.采用分区说明表进行. (2)或在程序运行过程,由用户指定申请与释放. (3)设计一个空闲区说明表,以保存某时刻主存空间占用情况.把空闲区说明表的变化情况以及各作业的申请.释

实验四、主存空间的分配和回收

实验四主存空间的分配和回收 专业:商软一班   姓名:董婷婷 学号:201406114105 1.目的和要求 1.1. 实验目的 用高级语言完成一个主存空间的分配和回收程序,以加深对动态分区分配方式及其算法的理解. 1.2. 实验要求 采用连续分配方式之动态分区分配存储管理,使用首次适应算法.循环首次适应算法.最佳适应算法和最坏适应算法4种算法完成设计. (1)**设计一个作业申请队列以及作业完成后的释放顺序,实现主存的分配和回收.采用分区说明表进行. (2)或在程序运行过程,由用户指定申请与释

操作系统之实验四主存空间的分配和回收

                                                                                    实验四主存空间的分配和回收                                                      专业:商业软件工程     班级:商软2班     姓名:甘佳萍     学号:201406114207 一.    目的和要求 1.1.           实验目的 用高级语言完成一个主存空间的

主存空间的分配和回收实验报告

实验四主存空间的分配和回收 网络工程专业   姓名:蔡利聪  学号:201306114117 一.目的和要求 1. 实验目的 用高级语言完成一个主存空间的分配和回收程序,以加深对动态分区分配方式及其算法的理解. 2.实验要求 采用连续分配方式之动态分区分配存储管理,使用首次适应算法.循环首次适应算法.最佳适应算法和最坏适应算法4种算法完成设计(任选两种算法). (1)设计一个作业申请队列以及作业完成后的释放顺序,实现主存的分配和回收.采用分区说明表进行. (2)或在程序运行过程,由用户指定申请与

【操作系统】主存空间的分配和回收

实验一.主存空间的分配和回收实验 专业:商业软件工程  姓名:杨晶晶  学号:201406114102 一.        实验目的 用高级语言完成一个主存空间的分配和回收程序,以加深对动态分区分配方式及其算法的理解. 二.        实验内容和要求 实验内容 根据指定的实验课题,完成设计.编码和调试工作,完成实验报告. 实验要求 采用连续分配方式之动态分区分配存储管理,使用首次适应算法.循环首次适应算法.最佳适应算法和最坏适应算法4种算法完成设计. (1)**设计一个作业申请队列以及作业完