Linux内存分配函数kmalloc、kzalloc、vmalloc、get_free_pages、malloc的区别

        在内核模块中申请分配内存需要使用内核中的专用API:kmalloc、vmalloc、kzalloc、kcalloc、get_free_pages;
对于提供了MMU功能的处理器而言,Linux提供了复杂的内存管理系统,使得进程所能访问到的地址空间可以达到4GB;而这4GB的空间又被划分为两个部分:0GB~3GB(PAGE_OFFSET,x86中的值是0xC0000000)的区域被用作进程的用户空间,3GB~4GB的区域被用作内核空间;在内核空间中,从3GB到vmalloc_start之间的这段地址区域作为物理内存映射区使用,该段映射区域内包含了内核镜像、物理页框表mem_map等等,比如,我们使用的系统物理内存为160MB,那么,3GB~3GB+vmalloc_start之间的区域就应该是映射的物理内存;在物理内存映射区域之后,就是虚拟内存vmalloc区域;对于160MB的系统而言,vmalloc_start的位置就应该在3GB+160MB位置附近(在物理内存映射区与vmalloc_start位置之间还存在一个8M的gap来防止越界),vmalloc_end的位置接近4GB的位置(系统会在最后的位置处保留一片128KB大小的区域专用于页面映射);

一、kmalloc
#include <linux/slab.h>
void *kmalloc(size_t size, gfp_t flags);

kmalloc() 申请的内存位于物理内存映射区域,而且在物理上也是连续的,它们与真实的物理地址只有一个固定的偏移,因为存在较简单的转换关系,所以对申请的内存大小有限制,不能超过128KB

较常用的 flags(分配内存的方法): 

 GFP_ATOMIC —— 分配内存的过程是一个原子过程,分配内存的过程不会被(高优先级进程或中断)打断;

GFP_KERNEL —— 正常分配内存

GFP_DMA —— 给 DMA 控制器分配内存,需要使用该标志(DMA要求分配虚拟地址和物理地址连续)。

flags 的参考用法: 
 |– 进程上下文,可以睡眠     GFP_KERNEL 
 |– 进程上下文,不可以睡眠    GFP_ATOMIC 
 |  |– 中断处理程序       GFP_ATOMIC 
 |  |– 软中断          GFP_ATOMIC 
 |  |– Tasklet           GFP_ATOMIC 
 |– 用于DMA的内存,可以睡眠   GFP_DMA | GFP_KERNEL 
 |– 用于DMA的内存,不可以睡眠  GFP_DMA |GFP_ATOMIC 


kmalloc申请得到的是物理内存,位于物理内存映射区,而且在物理地址上是连续的;但是kmalloc返回的内存地址却是虚拟地址(线性地址),返回的这个虚拟地址(线性地址)与真实的物理地址之间仅仅相差一个固定的偏移值;因此,kmalloc申请得到的物理内存块的首地址与其返回的虚拟地址之间存在着比较简单的转换关系;通过内核提供的函数virt_to_phys()可以实现该虚拟地址到真实的内核物理地址之间的转换:
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
static inline unsigned long virt_to_phys(volatile void* address)
{
 return __pa(address);
}
参数address是kmalloc返回的一个虚拟地址;该转换过程就是虚拟地址减去3GB(PAGE_OFFSET=0xC0000000);
一般情况下,PAGE_OFFSET=3*1024*1024*1024=0xC0000000(3G);
与之对应的函数就是phys_to_virt()用于把内核物理地址转换为虚拟地址:
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
static inline void * phys_to_virt(unsigned long address)
{
 return __va(address);
}
这两个函数都定义在include/asm-i386/io.h中;
kmalloc()函数用于小块内存的申请,最小可以申请的内存是32字节或64字节,最大可以申请的内存是128KB-16,其中,被减掉的16个字节用于存储页描述符结构;这些都依赖于体系架构所使用的页面大小;kmalloc申请的内存在物理地址上是连续的,这对于要进行DMA传输的设备来说,是非常重要的;
kmalloc()的内存分配是基于slab机制实现的,slab机制是为分配小内存而提供的一种高效的机制;但是slab机制也不是独立的,它本身也是在页分配器的基础上来划分更细粒度的内存供调用者使用;也就是说,系统先使用页分配器分配以页为最小单位的连续物理地址,然后,kmalloc()再在这个基础上根据调用者的需要进行切分的;另外,slab机制分配的内存在物理地址和虚拟地址(线性地址/逻辑地址)上都是连续的;
对于kmalloc()申请的内存,需要使用kfree()函数来释放;
备注:kmalloc是基于slab机制实现的;

二、kzalloc()

static inline void *kzalloc(size_t size, gfp_t flags);

kzalloc() 函数与 kmalloc() 非常相似,参数及返回值是一样的,可以说是前者是后者的一个变种,因为 kzalloc() 实际上只是额外附加了 __GFP_ZERO 标志。所以它除了申请内核内存外,还会对申请到的内存内容清零。

kzalloc() 对应的内存释放函数也是 kfree()。

三、vmalloc

void* vmalloc(unsigned long size)

vmalloc() 函数则会在虚拟内存空间给出一块连续的内存区,但这片连续的虚拟内存在物理内存中并不一定连续。由于 vmalloc() 没有保证申请到的是连续的物理内存,因此对申请的内存大小没有限制,如果需要申请较大的内存空间就需要用此函数了。

四、get_free_pages
#include <asm/pages.h>
fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)

参数gfp_mask用于指定申请内存时的控制方式,order用于指定申请的页数;它申请的内存位于(PAGE_OFFSET,HIGH_MEMORY)之间;
__get_free_pages()函数是页面分配器提供给调用者的最底层的内存分配函数,它申请的内存也是连续的物理内存,同样位于物理内存映射区;它是基于buddy机制实现的;在使用buddy机制实现的物理内存管理系统中,最小的分配粒度(单位)也是以页为单位的;在__get_free_pages()内部通过调用alloc_pages()来分配物理内存页;
__get_free_page()函数分配的是连续的物理内存,处理的是连续的物理地址,但是返回的也是虚拟地址(线性地址);如果想要得到正确的物理地址,也需要使用virt_to_phys()可进行转换;
对于__get_free_pages()函数申请的内存,需要使用__free_pages()函数来释放;
备注:__get_free_pages是基于buddy机制实现的;

五、总结

1).kmalloc/__get_free_pages申请的内存块都在物理内存映射区,即在(PAGE_OFFSET,HIGH_MEMORY)之间,处理的都是物理地址,且保证在物理地址空间上是连续的;二者返回的都是虚拟地址,如果需要得到正确的物理地址,需要使用virt_to_phys()进行转换;但是,kmalloc和vmalloc都是以字节为单位进行申请,而__get_free_pages()则是以页为单位进行申请;
2).vmalloc函数申请的内存块位于虚拟内存映射区,即在(VMALLOC_START,VMALLOC_END)之间,处理的都是虚拟内存,且保证在虚拟地址空间上是连续的,但是在物理地址空间上不要求连续;一般作为交换区、模块的内存使用;
3).kmalloc和vmalloc都是基于slab机制实现的,但是kmalloc的速度比vmalloc的速度快;__get_free_pages是基于buddy机制实现的,速度也较快;
4).kmalloc用于小块内存的申请,通常,一次所能申请的内存块的大小在(32/64字节,128KB-16)之间;而vmalloc可以用于分配大块内存的场合;
5).kmalloc申请的内存块在物理地址空间上是连续的,所以它申请的内存块可以直接用于DMA传输;vmalloc申请的内存块在虚拟地址空间上连续,但是在物理地址空间上不要求连续,所以它申请的内存块不能直接用于DMA传输;
6).kmalloc申请的内存块用kfree释放;vmalloc申请的内存块用vfree释放;__get_free_pages申请的内存页用__free_pages释放;
7).kmalloc申请得到的地址称为内核逻辑地址,vmalloc申请得到的地址称为内核虚拟地址;

 

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值