Chinaunix首页 | 论坛 | 博客
  • 博客访问: 190352
  • 博文数量: 48
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 262
  • 用 户 组: 普通用户
  • 注册时间: 2013-06-30 15:16
个人简介

本人无才,非专业出生,笔记仅记录学习,其中很多基于网络或他人原创,如有侵权和冒犯,或者错误,请谅解指正……谢谢!

文章存档

2016年(1)

2015年(3)

2013年(44)

我的朋友

分类: LINUX

2015-05-21 15:38:15

      实际的物理内存仅当进程真实访问新获取的虚拟地址时,才会由“请页机制”产生“缺页异常”,从而进入分配内存的程序“缺页异常”是虚拟内存机制的基本保证-----由它告诉内核为进程分配物理页,并建立页表。这样,虚拟地址才真实地映射了物理地址对于申请的内存必须释放,否则将寻致系统错误。

在内核中主要有以下几种内核分配函数,其主要区别和使用:

 

|--用户空间(动态分配)

--malloc /free 按字节分配内存

--new/delete

--vmalloc/free 分配的内存按页对齐

               

|--内核空间

-- kmalloc/kfree: 分配的内存物理上连续,只能在低端内存分配;            

-- vmalloc/vfree:分配的内存在内核空间中连续,物理上无需连续;

-- get_zeroed_page/free_page: 分配一个页面,并且该页面内容被清零,只能在低端内存分配;  
      -- __get_free_pages/free_pages:
分配指定页数的低端内存,只能从高端内存分配;

      --alloc_pages/__free_pages: 分配指定页数的内存,可以从高端内存,也可以从低端内存分配。

 

 

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

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

 

内存分配最终总是调用 __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 初始化。它们主要用  在文件系统和虚拟内存代码。那里允许分配休眠,但不应发生递归的文件系统调。

 

 

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

 

__GFP_DMA: 用于分配用于DMA(Direct memoryaccess-直接内存访问)功能的内存区(通常物理     地址在16M以下)

 

__GFP_HIGHMEM: 用于分配的内存可以位于高端内存的情况(通常在物理内存896M以上)

 

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

 

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

 

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

 

__GFP_REPEAT/__GFP_NOFAIL/__GFP_NORETRY:告诉分配器当满足一个分配有困难时,如何动作。     __GFP_REPEAT 表示努力再尝试一次,仍然可能失败;

__GFP_NOFAIL 告诉分配器尽最大努力 来满足要求,始终不返回失败,不推荐使用;

__GFP_NORETRY 告知分配器如果无法满足请求,立即返回。

 

 

内核内存的各个函数的异同如下表:
          


 

A、kmalloc/kfree函数:


    该函数分配的内存物理上连续,从ZONE_NORMAL区域返回连续内存的内存分配函数,只能在低端内存分配。

函数原型如下:

void *kmalloc(int count, int flags);

  count是要分配的字节数,flags是一个模式说明符。支持的所有标志列在include/linux./gfp.h文件中(gfpget free page的缩写),如下为常用标志。

  (1) GFP_KERNEL,被进程上下文用来分配内存。如果指定了该标志,kmalloc()将被允许睡眠,以等待其他页被释放。

(2) GFP_ATOMIC,被中断上下文用来获取内存。在这种模式下,kmalloc()不允许进行睡眠等待,以获得空闲页,因此GFP_ATOMIC分配成功的可能性比用GFP_KERNEL低。

使用kmalloc申请的内存需要kfree来释放,其函数原型如下:

    void kfree(const void *objp);

    其中参数objp: kmalloc返回的内核虚拟地址。

 

由于kmalloc()返回的内存保留了以前的内容,将它暴露给用户空间可到会导致安全问题,因此我们可以使用kzalloc()获得被填充为0的内存。

 

Bget_zeroed_page/free_page函数:

该函数分配一个页面,并且该页面内容被清零,只能在低端内存分配。函数原型如下:

unsigned long get_zeroed_page(gfp_t gfp_mask);

        参数说明:gfp_mask: 分配标志,用于控制kmalloc行为。

返回值: 指向分配到的已经被清零的内存页面第一个字节的指针,失败返回NULL

 

void free_pages(unsigned long addr);

参数addr: get_zeroed_page返回的内核虚拟地址。

 

下面通过一个实例来看看内核空间的动态内存申请方式,使用kmallocget_zeroed_page函数:

//Day01/KernelspaceDemo02.c

/*This demo is for how to use kmalloc and get_zeroed_page function in the kernel space*/

#include

#include

#include

#include

 

static char *buf_k=NULL;/*the data for kmalloc function*/

static char *buf_g=NULL;/*the data for get_zeroed_page function*/

 

/*initialize module function */

static int __init kernelspacedemo02_init(void){

    printk(KERN_ALERT "Entry kernelspacedemo02_init...\n");

 

    /*allocate memory from kernel space by kmalloc */

    buf_k=(char *)kmalloc(100,GFP_KERNEL);

    if(IS_ERR(buf_k)){

        printk(KERN_ALERT "Failure to allocate memory from kernel 100 Bytes ...\n");

        goto failure_alloc_buf_k;

    }

 

    memset(buf_k,0,100);

    strcpy(buf_k, "Congratulate ! Success to allocate memory from kernel 100 Bytes ...\n");

    printk(KERN_ALERT "Buffure  of buf_k:%s\n",buf_k);

 

 

    /*allocate memory from kernel space by get_zeroed_page */

    buf_g=(char *)get_zeroed_page(GFP_KERNEL);

    if(IS_ERR(buf_g)){

        printk(KERN_ALERT "Failure to allocate one page memory from kernel...\n");

        goto failure_alloc_buf_g;

    }

 

    strcpy(buf_g, "Congratulate ! Success to allocate  one page memory from kernel...\n");

    printk(KERN_ALERT "Buffure  of buf_g:%s\n",buf_g);

 

    return 0;

   

failure_alloc_buf_g:

    kfree(buf_k);

    buf_k=NULL;

failure_alloc_buf_k:

    return -ENOMEM;

 

}

 

/*clean up module function */

static void __exit kernelspacedemo02_exit(void){

    printk(KERN_ALERT "Entry kernelspacedemo02_exit...\n");

 

    /*free the  memory allocate by kmalloc and get_zeroed_page function */

    free_page((unsigned long )buf_g);

    buf_g=NULL;

    kfree(buf_k);

    buf_k=NULL;

}

/*The declaration of initialization and cleaning up module functions*/

module_init(kernelspacedemo02_init);

module_exit(kernelspacedemo02_exit);

 

/*The information of this module*/

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("JACK_ZHANG");

MODULE_DESCRIPTION("Use kmalloc and get_zeroed_page function to allocate memory in the kernel space");

MODULE_VERSION("0.0.1");

MODULE_ALIAS("KernelSpaceKmalloc");

 

编译,在开发板上运行,可以得到如下结果:

 

C__get_free_pages/free_pages函数:

该函数分配指定页数的低端内存,只能从高端内存分配。函数原型如下:

unsigned long __get_free_pages(gfp_t gfp_mask,unsigned int order);

 

参数说明:

gfp_mask:分配标志,用于控制__get_free_pages行为

    order: 请求或释放的页数的2的幂,例如:操作1页该值为0,操作16页该值为4

如果该值太大会寻致分配失败,该值允许的最大值依赖于体系结构。

 

返回: 指向分配到的连续内存区第一个字节的指针,失败返回NULL

 

free_pages的函数原型如下:

void free_pages(unsigned long addr, unsigned intorder);

    参数说明:

addr: __get_free_pages返回的内核虚拟地址

        order: __get_free_pages分配内存时使用的值

    int get_order(unsigned long size);

        size: 需要计算order值的大小(按字节计算)

        返回: __ get_free_pages等函数需要的order值。

 

 

Dalloc_pages/__free_pages函数:

该宏可以分配指定页数的内存,可以从高端内存,也可以从低端内存分配。

函数如下:

struct page *alloc_pages_node(gfp_t gfp_mask,unsigned int order);

        gfp_mask:分配标志,用于控制__get_free_pages行为。

        order: 请求或释放的页数的2的幂,例如:操作1页该值为0,操作16页该值为4

如果该值太大会寻致分配失败,该值允许的最大值依赖于体系结构

        返回: 指向分配到的物理页面中的第一个页的structpage指针,失败返回NULL

 

 

Evmalloc/vfree函数:

分配的内存在内核空间中连续,物理上无需连续。vmalloc由于不需要物理上也连续,所以性能很差。 一般只有在必须申请大块内存时才使用,例如动态插入模块时。

 

如果需要分配大的内存缓冲区,而且也不要求内存在物理上有联系,可以用vmalloc()代替kmalloc()

void *vmalloc(unsigned long size);

size: 待分配的内存大小,自动按页对齐

返回: 分配到的内核虚拟地址,失败返回NULL

 

vmalloc()需要比kmalloc()更大的分配空间,但是它更慢,而且不能从中断上下文调用。另外,不能用vmalloc()返回的物理上不连 续的内存执行DMA

在设备打开时,高性能的网络驱动程序通常会使用vmalloc()来分配较大的描述符环行缓冲区。

 

Vfree函数原型:

void vfree(const void *addr);

addr: vmalloc返回的内核虚拟地址。

 

下面的示例在内核模块中使用了kmalloc__get_free_pages,以及vmalloc函数,请注意区分其在实际使用的区别:

//Day01/KernelspaceDemo03.c

/*This demo is for how to use kmalloc  vmalloc and __get_free_pages function in the kernel address space*/

#include

#include

#include

#include

#include

 

static unsigned char *kernelkmalloc =NULL;/*the data for kmalloc function*/

static unsigned char *kernelpagemem=NULL;/*the data for __get_free_pages function*/

static unsigned char *kernelvmalloc=NULL;/*the data for vmalloc function*/

static int order =1;

 

 

/*initialize module function */

static int __init kernelspacedemo03_init(void){

    int ret=-ENOMEM;

   

    printk(KERN_ALERT "Entry kernelspacedemo03_init...\n");

 

    /*allocate memory from kernel space by kmalloc function*/

    kernelkmalloc=(unsigned char *)kmalloc(100,GFP_KERNEL);

    if(IS_ERR(kernelkmalloc)){

        printk(KERN_ALERT "Failure to allocate memory use kmalloc from kernel ...\n");

        goto failure_kernelkmalloc;

    }

 

 

    printk("Congratulate ! Success to allocate memory use kmalloc from kernel ...\n");

    printk(KERN_ALERT "The space address is : 0x%lx  \n",(unsigned long )kernelkmalloc);

 

 

    /*allocate memory from kernel space by __get_free_page function */

    kernelpagemem=(unsigned char *) __get_free_pages(GFP_KERNEL,order);

    if(IS_ERR(kernelpagemem)){

        printk(KERN_ALERT "Failure to get pages  from kernel...\n");

        goto  failure_kernelpagemem;

    }

 

    printk(KERN_ALERT"Congratulate ! Success to allocate free pages memory from kernel...\n");

    printk(KERN_ALERT "And this pages address:0x%lx\n",(unsigned long)kernelpagemem);

 

    /*allocate memory from kernel space by vmalloc function */

    kernelvmalloc=(unsigned char *) vmalloc (1024*1024);

    if(IS_ERR(kernelvmalloc)){

        printk(KERN_ALERT "Failure to get memory by vmalloc function from kernel...\n");

        goto  failure_kernelvmalloc;

    }

 

    printk(KERN_ALERT"Congratulate ! Success to allocate memory from kernel...\n");

    printk(KERN_ALERT "And this pages address:0x%lx\n",(unsigned long)kernelvmalloc);

 

 

    return 0;

   

failure_kernelvmalloc:

    free_pages((unsigned long)kernelpagemem,order);

    kernelpagemem=NULL;

   

failure_kernelpagemem:

    kfree(kernelkmalloc);

    kernelkmalloc=NULL;

   

failure_kernelkmalloc:

    return ret;

   

}

 

/*clean up module function */

static void __exit kernelspacedemo03_exit(void){

    printk(KERN_ALERT "Entry kernelspacedemo03_exit...\n");

 

    /*free the  memory allocate by kmalloc/ __get_free_pages/vmalloc  function */

    vfree(kernelvmalloc);

    kernelvmalloc=NULL;

    free_pages((unsigned long)kernelpagemem,order);

    kernelpagemem=NULL;

    kfree(kernelkmalloc);

    kernelkmalloc=NULL;

   

}

/*The declaration of initialization and cleaning up module functions*/

module_init(kernelspacedemo03_init);

module_exit(kernelspacedemo03_exit);

 

/*The information of this module*/

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("JACK_ZHANG");

MODULE_DESCRIPTION("Use kmalloc/ get_free_pages/vmalloc function to allocate memory in the kernel space");

MODULE_VERSION("0.0.1");

MODULE_ALIAS("KernelAddressSpace");

该范例编译后,开发板上运行效果如图:

 

 

 

特别申明:

在内核中特别注意,对于申请的内存使用完毕必须释放,否则将导致系统错误。

   

在内核中常使用的内存申请大致代码范例如下,实际中依据需要修改。


范例:

//Day01/KernelspaceDemo01.c

#include

#include

#include

#include

#include

#define PAGE_NUM 4

 

unsigned char * kernelkmalloc = NULL;

unsigned char * kernelkpagemen = NULL;

unsigned char * kernelvmalloc = NULL;

 

static int  kernelspace_init (void){

        /*错误编号*/

        int ret= -ENOMEM;

       

        /*kmalloc的第二个参数flags

         * GFP_KERNEL:分配内存,分配过程中可能导致睡眠,没有分配到就睡眠等待

         * GFP_ATOMIC:分配过程中不会导致睡眠,没有分配到就直接返回

         * __GFP_DMA :申请到的内存通常位于0——16M区间

         * __GFP_HIGHMEM :申请高端内存 (896M以上的物理地址)

        */

        kernelkmalloc = (unsigned char *)kmalloc(100,GFP_KERNEL);

 

        /*判断内存是否申请分配成功。如果成功该宏返回的是分配成功的地址,出错则回的错误编号*/

        if(IS_ERR(kernelkmalloc)){

          printk("Kmalloc failed !\n");

          ret = PTR_ERR(kernelkmalloc);

          goto failure_kmalloc;/*使用goto语句做出错处理直接返回*/

        }

        printk("kmalloc space :0X%lx \n",(unsigned long )kernelkmalloc);

 

        /*__get_free_page的第二个参数order:

         *    请求或释放的页数的2的幂(比如是4,表示分配16页的空间)

         *    一般先定义宏,程序逻辑上容易理解

         */

        kernelkpagemen = (unsigned char *)__get_free_pages(GFP_KERNEL,PAGE_NUM);

       

        /* 在内核空间程序,如果第一次申请成功、第二次失败,返回的时候须要释放第一次分配的内存

         * 如果在应用空间,进程结束,所申请和打开的资源,系统会自动回收和关闭

         * 这一点在内核编程中尤其重要,否则会发生内存泄漏等问题

         */

        if(IS_ERR(kernelkpagemen)){

          printk("Kmalloc failed !\n");

          ret = PTR_ERR(kernelkmalloc);

          goto failure_get_free_page ;/*使用goto语句做出错处理直接返回*/

        }

        printk("get_free_page :0X%lx \n",(unsigned long )kernelkpagemen);

       

        kernelvmalloc = (unsigned char *)vmalloc(1024*1024);

        if(IS_ERR(kernelvmalloc)){

          printk("vmalloc failed !\n");

          ret = PTR_ERR(kernelvmalloc);

          goto failure_vmalloc ;/*使用goto语句做出错处理直接返回*/

        }

        printk("vmalloc address :0X%lx \n",(unsigned long )kernelvmalloc);

 

        return 0;

 

/*出错处理*/

failure_vmalloc:

        free_pages((unsigned long)kernelkpagemen,PAGE_NUM);

failure_get_free_page:

        kfree(kernelkmalloc);/*若执行到此处,也会执行下面的return ret*/

failure_kmalloc:

        return ret;

 

}

 

/*模块卸载的时候,需要释放所申请的内存*/

static void  kernelspace_exit (void){

        vfree(kernelvmalloc);

        free_pages((unsigned long)kernelkpagemen,PAGE_NUM);

        kfree(kernelkmalloc);

}

 

MODULE_LICENSE ("Dual BSD/GPL");

module_init(kernelspace_init);

module_exit(kernelspace_exit);

而基于客户端的代码则如下:

/*This demo is for how to use malloc function in the userspace*/

#include

#include

#include

 

static char *dat =NULL;

static long allcnt =50*1024*1024;

 

int main( int argc,char **argv){

    char ch;

    printf("Starting demostration:\n");

    printf("Press \'a\' key to allocate space...\n");

    ch =getchar();

    if('a'!=ch){

        printf("You press error key !\n");

        exit(-1);

    }

 

    /*allocate memory*/

    dat=malloc(allcnt);

    if(NULL==dat){

        printf("Failure to allocate memory size:%ld...\n",allcnt);

        exit(-2);

    }

    printf("Success to allocate memory size:%ld...\n",allcnt);

 

    /*Using memory allocate*/

    printf("Press \'u\' key to use memory ...\n");

    ch=getchar();

    if('u'!= ch){

        printf("You press error key !\n");

        free(dat);

        exit(-3);

    }

    printf("Using memory allocated...\n");

    memset(dat,0,allcnt);

 

    /*Free memory and exit*/

    printf("Press any key to exit...\n");

    getchar();

    free(dat);

    dat=NULL;

 

    return 0;

}

编译后运行效果如下:

 

 

      此外内核还提供了一些更复杂的内存分配技术,包括后备缓冲区(look aside buffer)slabmempool等。

阅读(5436) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~