IMX257 Linux内存空间内存分配

发布时间:2024-08-15  

一、KMALLOC

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

#include

void *kmalloc(size_t size, int flags);

参数详解:

size 参数

 

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

 

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

 

flags 参数

 

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

所有标志都定义在 ,有符号代表常常使用的标志组合。

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

 

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分配器,相关函数和类型在中申明。

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{

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

  3 #include

  4 #include

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 #include

 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!nn');

 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!nn');

 44     return 1;

 45 }

 46 

 47 static int  key_release(struct inode *inode, struct file *filp)

 48 {

 49     printk('<0>function write!nn');

 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!nn');

 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!nn',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 !nn');

 82         return -1;

 83     }

 84     printk('<0>buf1 kmalloc mem addr = %x nn',buf1);

 85     memset(buf1,0,100);                                //将所分配到的内存清理

 86     strcpy(buf1,'<<------buf1 Kmalloc Mem OK!------>>');//向内存中写入数据

 87     printk('<0>BUF1: %snn',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 !nn');

 93         return -1;

 94     }

 95     printk('<0>buf2 get_zeroed_page addr = %x nn',buf2);

 96     strcpy(buf2,'<<------buf2 get_zeroed_page OK!------>>');//向内存中写入数据

 97     printk('<0>BUF2: %snn',buf2);

 98 

 99     //Vmalloc 申请1000000个字节(1M)的空间 虚拟地址分配(零碎的物理地址空间)

100     buf3 = (unsigned char *)vmalloc(1000000);

101     if(buf3 == NULL){

102         printk('<0>buf3 vmalloc error !nn');

103         return -1;

104     }

105     printk('<0>buf3 vmalloc addr = %x nn',buf3);

106     strcpy(buf3,'<<------buf3 vmalloc OK!------>>');//向内存中写入数据

107     printk('<0>BUF3: %snn',buf3);

108     

109     return 0; 

110 }

111                      

112 static void __exit key_irq_exit(void)

113 {

114     printk('<0>nGoodbye,%s!nn',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 nn',buf1);

122     kfree(buf1);

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    内存条,组成双通道16G,看测试表现如何吧。 通过鲁大师查看硬件参数,内存16GB。 鲁大师查看内存信息,两个内存条简单信息如图。 CPU-Z......
    没有 .ssh 这个目录就 mkdir 创建一个。   5.安装dnw 先配置驱动开发环境,为后边编译usb驱动做准备。 查看内核版本: uname -r sudo apt install linux......
    ”——“Memory1”; 在“Address”地址栏中输入MSP的值:0x20001288,然后在对应的行里找到地址。 地址一般以0x08开头的32位数。本例中,地址为0x08003CB9。 内存信息 1.4......
    ”——“Memory1”; 在“Address”地址栏中输入MSP的值:0x20001288,然后在对应的行里找到地址。 地址一般以 0x08 开头的32位数。本例中,地址为0x08003CB9。 内存信息 在Keil......
    gcc代码反汇编查看内存分布[1]: gcc;# gcc -v gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5) 重点: 代码中的内存......
    于编译产生的.o文件, 如果你运行'make clean' or 'make distclean',这个文件就会被清除掉(详情查看内核Makefile)。因此,如果再利用此内核源码编译内核模块 而我......
    关于STM32 ST-LINK Utility查看内核运行状态,助你判断程序是否跑飞;本文围绕STM32 ST-LINK Utility讲几点主要功能及相关拓展知识: 1.STM32编程下载; 2......
    使用了正确的库文件。 确认是否存在多线程访问 AES-GCM 的函数会在几个线程中调用,而且确认不会出现同时调用的情况,不存在 raise condition 的问题。 查看内存使用情况 最初怀疑是否因为任务栈溢出造成,于是查看内存......
    使用了正确的库文件。 确认是否存在多线程访问 AES-GCM 的函数会在几个线程中调用,而且确认不会出现同时调用的情况,不存在 raise condition 的问题。 查看内存使用情况 最初......
    STM32调试诊断工具 | ST-LINK Utility查看内核运行状态;ST-LINK Utility 是一款针对 STM32 的下载编程工具,目前它被 STM32CubeProgrammer......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>