驱动基础之分配内存
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了驱动基础之分配内存相关的知识,希望对你有一定的参考价值。
1 kmalloc()函数原型如下所示:
void *kmalloc(size_t size,int gfp_mask);
该函数是一个简单的接口,用它可以获得以字节为单位的内存。该函数返回一个指向内存块的指针,其内存块至少有size大小。所分配的内存区在物理地址上是连续的。出错时,它返回NULL。除非没有足够的内存可用,否则内核总能分配成功。
通过kmalloc()获得的物理内存应该被kfree()释放,函数原型如下所示:
void kfree(const void *addr);
如果想要释放的内存不是由kmalloc()分配的,或者想要释放的内存早就释放了,此时调用kfree()会导致严重的后果。与用户空间类似,分配和回收要配对使用,以避免内存泄露和其他BUG,注意调用kfree(NULL)是安全的。
gfp_mask 表示分配标志符,有如下常用标志:
GFP_KERNEL 内核内存的常用分配方法,可能引起休眠。使用
GFP_KERNEL允许kmalloc()在空闲内存较少时把当前进程转入休眠以等待一个页面。在当前进程休眠时,内核会采取适当的行动,或者是把缓冲区的内容刷新到硬盘上,或者是从一个用户进程换出内存,以获取一个内存页面。
GFP_ATOMIC 用于中断处理例程、tasklet、持有自旋锁以及其他不能休眠的地方。内核通常会为原子性的分配预留一些空闲页面。使用GFP_ATOMIC标志时,kmalloc()甚至可以用掉最后一个空闲页面。
2 __get_free_pages()
函数原型如下所示:
unsigned long __get_free_pages(unsigned int gfp_mask,
unsigned long order)
该函数分配(1<<order)个连续的物理页,并且返回分配内存的首地址,分配的内存页不清零。内存页释放函数如下所示:
void free_pages(unsigned long addr,unsigned long order)
释放页时要谨慎,只能释放属于你的页,传递了错误的地址或者错误的order值,都可能导致系统崩溃。
order 表示分配的页数是(1<<order),order可允许最大的值是10或者11(对应于1024或者2048个页),这依赖于体系结构。
kmalloc()和__get_free_pages()的异同:
kmalloc()和__get_free_pages()申请的内存位于DMA映射区和常规区域映射区,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因此存在较简单的转换关系。
基于页的分配策略在于更有效地使用了内存。按页分配不会浪费内存空间,而用kmalloc()则会因为分配粒度的原因浪费一定数量的内存。
3 vmalloc()
函数原型如下所示:
void *vmalloc(unsigned long size);
大多数情况下,只有硬件设备需要获得物理地址连续的内存。在很多体系结构上,硬件设备存在于内存管理单元MMU之外,它根本不理解什么是虚拟地址。因此,硬件设备用到的任何内存区都必须是物理地址连续的内存,而不仅仅是虚拟地址连续的内存。
很多内核代码都使用kmalloc()来获得内存,而不是vmalloc()。这主要是出于性能的考虑,vmalloc()为了把物理上不连续的页转换为虚拟地址空间连续的页,必须专门建立页表项。因为这些原因,vmalloc()仅仅在不得已时才会使用,一般是为了获得大块内存时,比如模块被动态插入到内核中时,就把模块装载到由vmalloc()分配的内存上。
vmalloc()在<linux/vmalloc.h>中声明,在<mm/vmalloc.c>中定义。
vmalloc()返回一个指针,指向虚拟地址连续的内存,其大小至少为size。在发生错误时,函数返回NULL。vmalloc()可能睡眠,因此不能在中断上下文中进行调用。通过vmalloc()获得的虚拟内存应该被vfree()释放,函数原型如下所示:
void vfree(void *addr);
vfree()可能睡眠,因此不能在中断上下文中进行调用。
4 ioremap()
函数原型如下所示:
void *ioremap(unsigned long offset, unsigned long size)
ioremap()与vmalloc()类似,也需要建立新的页表,但是它并不进行vmalloc()中所执行的内存分配行为。ioremap()返回一个特殊的虚拟地址,该虚拟地址位于vmalloc映射区域。通过ioremap()获得的虚拟地址应该被iounmap()释放,函数原型如下所示:
void iounmap(void *addr);
ioremap()有个变体是devm_ioremap(),类似于其他以devm_开头的函数,通过devm_ioremap()进行的映射通常不需要在驱动退出和出错时进行iounmap()。devm_ioremap()函数原型如下所示:
void __iomem *devm_ioremap(struct device *dev, unsigned long offset,
unsigned long size);
以上是关于驱动基础之分配内存的主要内容,如果未能解决你的问题,请参考以下文章