原型

kmalloc的原型如下:

 void *kmalloc(size_t size, gfp_t flags)

第一个参数是要分配的块的大小,第二个参数是分片标志;

flags标志

最常用的标志是GFP_KERNEL,表示内存分配(最终总是调用get_free_page来实现实际的分配,这就是GFP_前缀的由来)是代表在内核空间的进程执行的;换句话说,这意味着调用它的函数正代表某个进程执行系统调用;使用GFP_KERNEL允许在kmalloc在空闲内存较少的时候把当前进程转入休眠以等待一个页面;因此,使用GFP_KERNEL分配内存的函数必须是可重入的;在当前进程休眠时,内核会采取适当的行动,或者把缓冲区的内容刷到硬盘上,或者从搞一个用户进程换出内存,已获得一个内存页面;

GFP_KERNEL分配标志并不是始终适用,有时kmalloc是在进程上下文之外被调用的,例如中断处理程序,tasklet以及内核定时器中调用;这种情况下current进程就不该休眠,驱动程序应该换用GFP_ATOMIC标志;内核通常会为原子性的分配预留一些空闲页面;使用GFP__ATOMIC标志时,kmalloc甚至可以用掉最后一个空闲页面;不过如果连最后一页都没有了,分配就返回失败;

除了GFP_KERNEL和GFP_ATOMIC外,还有一些其他标志可用于替换或者补充这两个标志,不过这两个标志已经可以满足大多数驱动程序的需要了;

下面罗列这些标记:

GFP_ATOMIC-在中断处理程序或者其他运行与进程上下文之外的代码中分配内存,不能休眠;

GFP_KERNEL-内核内存的通常分配方法,可能引起休眠;

GFP_USER-为用户空间页分配内存,可能会休眠;

GFP_HIGHUSER-类似GFP_USER,不过如果有高端内存的话,就从那里分配;

GFP_NOIO GFP_NOFS-这两个标志类似GFP_KERNEL,但是为内核分配内存的工作方式添加了一些限制,具有GFP_NOFS标志的分配不允许执行任何文件系统调用,而GFP_NOIO禁止任何IO的初始化;这两个标志主要在文件系统和虚拟内存代码中使用,这项代码中的分配内存可休眠,但不应该发生递归的文件系统调用;

上面列出的分配标志可以和下面的标志“或”起来使用;下面这些标志控制如何进行分配:

__GFP_DMA-标志请求分配发生在可进行DMA的内存区段中;

__GFP_HIGHMEM-表明要分配的内存可位于高端内存;

__GFP_COLD-通常,内存分配器会试图返回缓存热页面,即可在处理器缓存中找到的页面;相反,这个标志请求尚未使用的冷页面;对用于DMA读取的页面分配,可以使用这个标志,因为这种情况下,页面存在于处理器缓存中没有多大帮助;

__GFP_NOWARN-该标志很少使用;它可以避免内核在无法满足分配请求的时候产生经过;

__GFP_HIGH-该标志标记了一个高优先级的请求,它允许为紧急状况而消耗有内核保留的最后一个页面;

__GFP_REPEAT、__GFP_NOFAIL、__GFP_NORETRY-上述标志告诉分配器在满足分配请求而遇到困难时应该采取何种行动;__GFP_REPEAT表示努力再尝试一次,它会重新尝试分配,当仍有可能失败;__GFP_NOFAIL标志告诉分配器始终不返回失败,它会努力满足分配请求;不鼓励使用这个标志;__GFP_NORETRY告诉分配器,如果请求的内存不可获得,就立即返回;

内存区段

__GFP_DMA和__GFP_HIGHMEM的使用与平台相关,尽管在所有平台上都可以使用这两个标志;

Linux内核把内存分成三个区段:可用于DMA的内存,常规内存以及高端内存;通常的内存分配都放生在常规内存区,但通过设置上面的标志可以把请求在其他区段中分配;其思路是每种计算平台都必须知道如何把自己特定的内存范围归类到这三个区段中,而不是认为所有的RAM都一样;

可用于DMA的内存是指存在于特别地址范围内的内存,外设可以利用这些内存执行DMA访问;在大多数健全的系统上,所有内存都位于这一区段;在x86平台上,DMA区段是RAM的前16MB;PCI设备无此限制;

高端内存是32位平台为了访问大量的内存而存在的一种机制;如果不首先完成一些特殊的映射,我们就无法从内核中直接访问这些内存,因此通常较难处理;但是,如果驱动程序要使用大量的内存,那么在能够使用高端内存的大系统上可以工作的更好;

当一个页面为了满足kmalloc的要求被分配时,内核会创建一个内存区段的列表以供搜索;如果指定了__GFP_DMA标志,则只有DMA区段会被搜索;如果低端地址上没有可用内存,就会分配失败;如果没有指定特定的标志,则常规区段和DMA区段都会被搜索;而如果设置了__GFP_HIGHMEM标志,则所有三区段都会被搜索以及获取一个空闲页面,然而要注意的是,kmalloc不能分配高端内存;

size参数

内核负责管理系统物理内存,物理内存只能按页面进行分配;其结果是kmalloc和典型的用户空间的malloc在实现上有很大的差别;简单的基于堆的分配技术会遇到麻烦,因为页面编辑的处理成为一个很棘手的问题;因此内核使用了特殊的基于页的分配技术,以及最佳地利用系统RAM;

Linux处理内存分配的方法是,创建一系列的内存池对象,每个池中的内存块大小是固定一致的;处理分配请求时,就直接在包含有足够大的内存块的池中传递一个整块请求者;

驱动程序开发应该牢记的就是内核值分配一些预定义的,固定大小的字节数组;如果申请任意数量的内存,那么得到的很可能会多一些,最多会到申请数量的两倍;另外,kmalloc能处理的最小的内存块是32或者64,到底是哪个则取决于当前体系结构使用的页面大小;

对kmalloc能够分配的内存块的大小,存在一个上限;这个限制随着体系架构的不同以及内存配置选项的不同而变化;如果希望代码有完整的可移植性,则不应该分配大于128K的内存;但是,如果希望获得多余几千字节的内存,则最好是用除kmalloc之外的内存获取方法;

Linux设备驱动程序 之 kmalloc的更多相关文章

  1. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  2. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  3. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  4. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  5. 教你写Linux设备驱动程序:一个简短的教程

    教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html

  6. linux设备驱动程序_hello word 模块编译各种问题集锦

    在看楼经典书籍<linux设备驱动程序>后,第一个程序就是编写一个hello word 模块. 原以为非常easy,真正弄起来,发现问题不少啊.前两天编过一次,因为没有记录,今天看的时候又 ...

  7. Linux设备驱动程序学习----1.设备驱动程序简介

    设备驱动程序简介 更多内容请参考Linux设备驱动程序学习----目录 1. 简介   Linux系统的优点是,系统内部实现细节对所有人都是公开的.Linux内核由大量复杂的代码组成,设备驱动程序可以 ...

  8. Linux设备驱动程序学习----2.内核模块与应用程序的对比

    内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...

  9. Linux设备驱动程序学习----3.模块的编译和装载

    模块的编译和装载 更多内容请参考Linux设备驱动程序学习----目录 1. 设置测试系统 第1步,要先从kernel.org的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...

随机推荐

  1. 03 Django之视图函数

    一.Django的视图函数view 一个视图函数(类),简称视图,是一个简单的Python函数(类),它接受WEB请求并返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误, ...

  2. Redis 学习-Redis Sentinel

    一.启动服务 1. 配置文件 sentinel.conf daemonize yes # 是否守护进程启动 pidfile "/var/run/redis-sentinel-26379.pi ...

  3. 面试总结关于Spring面试问题(精选)

    1.什么是Spring? Spring是一个反转控制IOC和AOP的开发框架和平台. 2.解释一下Spring? 轻量 : Spring 在大小和透明度上是轻量的,Spring基本核心版本大概只有1M ...

  4. Visual Studio 2017使用

    常用快捷方式 Ctrl + KK插入书签 取消书签Ctrl + KP 上一个书签Ctrl + KN 下一个数千Ctrl + F3 先一个关键词Shift + F3 上一个关键词 Ctrl + KC 添 ...

  5. 主板(motherboard)

    若转载请于明显处标明出处:http://www.cnblogs.com/kelamoyujuzhen/p/8979262.html 整台PC都是围绕主板(motherboard)构建的,它是PC中最重 ...

  6. 2.数码相框-编码(ASCII/GB2312/Unicode)介绍

    转载:https://www.cnblogs.com/lifexy/p/8485634.html 在上章-学习了数码相框的框架分析(1)了 本章主要内容如下: 1)熟悉ASCII/GB2312/Uni ...

  7. 【转】优秀的Go开源项目

    http://www.mhtclub.com/post/60   目录 优秀的Go开源项目 中文Go语言学习教程 国外的Go语言教程 openbilibili源码 Go作为Google2009年推出的 ...

  8. vue-cli3 将自己写的组件封装成可引入的js文件

    一.调整项目结构 首先用 vue-cli 创建一个 default 项目 // 顺便安利一篇文章<Vue 爬坑之路(十二)—— vue-cli 3.x 搭建项目> 当前的项目目录是这样的: ...

  9. 12、生命周期-@Bean指定初始化和销毁方法

    12.生命周期-@Bean指定初始化和销毁方法 Bean的生命周期:创建->初始化->销毁 容器管理bean的生命周期 我们可以自定义初始方法和销毁方法,容器在bean进行到当期那生命周期 ...

  10. 第八章 用SQL语句操作数据

    --切换数据库:手动切换和命令切换 use MySchool --向Student表中插入数据 --语法:INSERT [INTO] 表名 (列名) VALUES (值列表) --注意事项: --1. ...