Linux内核中内存相关的操作函数-1_dma_alloc_coherent dma dma32-程序员宅基地

技术标签: 其它  



1.原理说明
  Linux内核中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示。四级页表分别为:
  * 页全局目录(Page Global Directory)
  * 页上级目录(Page Upper Directory)
  * 页中间目录(Page Middle Directory)
  * 页表(Page Table)
  页全局目录包含若干页上级目录的地址,页上级目录又依次包含若干页中间目录的地址,而页中间目录又包含若干页表的地址,每一个页表项指 向一个页框。Linux中采用4KB大小的 页框作为标准的内存分配单元。
  多级分页目录结构
  1.1.伙伴系统算法
  在实际应用中,经常需要分配一组连续的页框,而频繁地申请和释放不同大小的连续页框,必然导致在已分配页框的内存块中分散了许多小块的 空闲页框。这样,即使这些页框是空闲的,其他需要分配连续页框的应用也很难得到满足。
  为了避免出现这种情况,Linux内核中引入了伙伴系统算法(buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连 续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍。
  假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个
页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。
  页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。
  1.2.slab分 配器
  slab分配器源于 Solaris 2.4 的 分配算法,工作于物理内存页框分配器之上,管理特定大小对象的缓存,进行快速而高效的内存分配。
  slab分配器为每种使用的内核对象建立单独的缓冲区。Linux 内核已经采用了伙伴系统管理物理内存页框,因此 slab分配器直接工作于伙伴系 统之上。每种缓冲区由多个 slab 组成,每个
slab就是一组连续的物理内存页框,被划分成了固定数目的对象。根据对象大小的不同,缺省情况下一个 slab 最多可以由 1024个页框构成。出于对齐 等其它方面的要求,slab 中分配给对象的内存可能大于用户要求的对象实际大小,这会造成一定的 内存浪费。
  2.常用内存分配函数
  2.1.__get_free_pages
  unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
  __get_free_pages函数是最原始的内存分配方式,直接从伙伴系统中获取原始页框,返回值为第一个页框的起始地址。
__get_free_pages在实现上只是封装了alloc_pages函 数,从代码分析,alloc_pages函数会分配长度为1<
  2.2.kmem_cache_alloc
  struct kmem_cache *kmem_cache_create(const char *name, size_t size,
  size_t align, unsigned long flags,
  void (*ctor)(void*, struct kmem_cache *, unsigned long),
  void (*dtor)(void*, struct kmem_cache *, unsigned long))
  void *kmem_cache_alloc(struct kmem_cache *c, gfp_t flags)
  kmem_cache_create/ kmem_cache_alloc是基于slab分配器的一种内存分配方式,适用于反复分配释放同一大小内存块的场合。首先用kmem_cache_create创建一个高速缓存区域,然后用kmem_cache_alloc从 该高速缓存区域中获取新的内存块。kmem_cache_alloc一次能分配的最大内存由mm/slab.c文件中的MAX_OBJ_ORDER宏 定义,在默认的2.6.18内核版本中,该宏定义为5, 于是一次最多能申请1<<5 * 4KB也就是128KB的 连续物理内存。分析内核源码发现,kmem_cache_create函数的size参数大于128KB时会调用BUG()。测试结果验证了分析结果,用kmem_cache_create分 配超过128KB的内存时使内核崩溃。
  2.3.kmalloc
  void *kmalloc(size_t size, gfp_t flags)
  kmalloc是内核中最常用的一种内存分配方式,它通过调用kmem_cache_alloc函
数来实现。kmalloc一次最多能申请的内存大小由include/Linux/Kmalloc_size.h的
内容来决定,在默认的2.6.18内核版本中,kmalloc一 次最多能申请大小为131702B也就是128KB字
节的连续物理内存。测试结果表明,如果试图用kmalloc函数分配大于128KB的内存,编译不能通过。
  2.4.vmalloc
  void *vmalloc(unsigned long size)
  前面几种内存分配方式都是物理连续的,能保证较低的平均访问时间。但是在某些场合中,对内存区的请求不是很频繁,较高的内存访问时间也
可以接受,这是就可以分配一段线性连续,物理不连续的地址,带来的好处是一次可以分配较大块的内存。图3-1表 示的是vmalloc分配的内存使用的地址范围。vmalloc对 一次能分配的内存大小没有明确限制。出于性能考虑,应谨慎使用vmalloc函数。在测试过程中, 最大能一次分配1GB的空间。
  Linux内核部分内存分布
  2.5.dma_alloc_coherent
  void *dma_alloc_coherent(struct device *dev, size_t size,
  ma_addr_t *dma_handle, gfp_t gfp)
  DMA是一种硬件机制,允许外围设备和主存之间直接传输IO数据,而不需要CPU的参与,使用DMA机制能大幅提高与设备通信的
吞吐量。DMA操作中,涉及到CPU高速缓 存和对应的内存数据一致性的问题,必须保证两者的数据一致,在x86_64体系结构中,硬件已经很 好的解决了这个问题, dma_alloc_coherent和__get_free_pages函数实现差别不大,前者实际是调用__alloc_pages函 数来分配内存,因此一次分配内存的大小限制和后者一样。__get_free_pages分配的内存同样可以用于DMA操作。测试结果证明,dma_alloc_coherent函 数一次能分配的最大内存也为4M。
  2.6.ioremap
  void * ioremap (unsigned long offset, unsigned long size)
  ioremap是一种更直接的内存“分配”方式,使用时直接指定物理起始地址和需要分配内存的大小,然后将该段 物理地址映射到内核地址空间。ioremap用到的物理地址空间都是事先确定的,和上面的几种内存
分配方式并不太一样,并不是分配一段新的物理内存。ioremap多用于设备驱动,可以让CPU直接访问外部设备的IO空间。ioremap能映射的内存由原有的物理内存空间决定,所以没有进行测试。
  2.7.Boot Memory
  如果要分配大量的连续物理内存,上述的分配函数都不能满足,就只能用比较特殊的方式,在Linux内 核引导阶段来预留部分内存。
  2.7.1.在内核引导时分配内存
  void* alloc_bootmem(unsigned long size)
  可以在Linux内核引导过程中绕过伙伴系统来分配大块内存。使用方法是在Linux内核引导时,调用mem_init函数之前 用alloc_bootmem函数申请指定大小的内存。如果需要在其他地方调用这块内存,可以将alloc_bootmem返回的内存首地址通过EXPORT_SYMBOL导 出,然后就可以使用这块内存了。这种内存分配方式的缺点是,申请内存的代码必须在链接到内核中的代码里才能使用,因此必须重新编译内核,而且内存管理系统看不到这部分内存,需要用户自行管理。测试结果表明,重新编译内核后重启,能够访问引导时分配的内存块。
  2.7.2.通过内核引导参数预留顶部内存
  在Linux内核引导时,传入参数“mem=size”保留顶部的内存区间。比如系统有256MB内 存,参数“mem=248M”会预留顶部的8MB内存,进入系统后可以调用ioremap(0xF800000,0x800000)来申请这段内存。

        3几种分配函数的比较


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mshatanshangdeyu/article/details/46624787

智能推荐

关于C++中vector结构的使用和它的使用函数_结构体 vector 函数调用-程序员宅基地

文章浏览阅读335次。这是来源于c++标准模板库STL中的一种顺序结构关于顺序结构还有两种即:c++list 和 c++double-ended queuesVectors 包含着一系列连续存储的元素,其行为和数组类似。访问Vector中的任意元素或从末尾添加元素都可以在常量级时间复杂度内完成,而查找特定值的元素所处的位置或是在Vector中插入元素则是线性时间复杂度。vectors相当于一种容器C..._结构体 vector 函数调用

《编程导论(Java) ·10.3》补充:递归的优化_java中写递归算法的书籍-程序员宅基地

文章浏览阅读1.9k次。递归强大、优雅、易实现...问题是效率和栈溢出(java.lang.StackOverflowError)。为什么Scheme不需要迭代结构如while、for呢?在Java编译器不直接支持尾调用优化 tail-call optimization (TCO)的情况下,如何使用lambda表达式的延迟计算或者直接使用流来优化递归?递归会导致Java中迭代的消失吗?_java中写递归算法的书籍

ApiBoilerPlate:构建ASP.NET Core 3 API的新功能和改进-程序员宅基地

文章浏览阅读243次。介绍 (Introduction)Two months ago, ApiBoilerPlate was first released and it’s incredible to see that the template garnered hundreds of installs within a short period of time. I’m very glad that it som..._asp.net core boilerplate

Linux —— docker 把web项目放进tomcat中,访问是404_docker docbase填写映射目录,404-程序员宅基地

文章浏览阅读1.6k次。情况: docker启动tomcat容器,地址映射,目录映射后,访问对应的地址http://192.168.88.129:8080/Maven_SSM报 404,http://192.168.88.129:8080的猫也看不到 启动tomcat 容器命令: docker run -di --name=mytomcat -p 808..._docker docbase填写映射目录,404

bzoj3578: GTY的人类基因组计划2-程序员宅基地

文章浏览阅读60次。STL即玄学。。一看过去就觉得要hash一下吧,顺便瞄了一眼hint那我就想mp记录hash值咯然而修改不好搞啊。。。暴力重算肯定挂啊。。%了一下发现原来可以用异或来离散化(感觉这个很看脸的样子)把没有重复插入set,这样就不用穷举区间啦~边查找边删除不好弄啊。。这个RE了很久,s.end()!=it这个判法还是很迷。。。#include<cstdio>..._p2075 gty的人类基因组计划

计算机网络面临的威胁_计算机网络系统面临的威胁-程序员宅基地

文章浏览阅读3.1k次。计算机网络面临的威胁多种多样,概括起来主要有以下几类:   (1)内部泄密和破坏    内部人员可能对信息网络形成的威胁包括:内部泄密人员有意或无意泄密、更改记录信息;内部非授权人员有意偷窃机密信息、更改记录信息;内部人员破坏信息系统等。   (2)截收    网络攻击者可能通过搭线或在电磁波辐射范围内安装截收装置等方式,截获机密信息,或通过对信息流量和流向、通信频度和长度等参_计算机网络系统面临的威胁

随便推点

常见xss绕过——xss-lab练习_xss lib 绕过-程序员宅基地

文章浏览阅读510次。0X00网安菜鸡为了学习xss漏洞攻击,选择了xss game来进行一些xss攻击绕过的学习。0X01 level 1首先进来后观察到这样的页面:注意到url中的参数name,值为test,页面中出现了test,尝试修改name的值发现页面中内容改变,于是找到了输入输出的地方。首先将url栏处修改为?name=<script>alert('1')</script>,成功过关。0X02 level 2进来后发现有个输入框,尝试输入<script>aler_xss lib 绕过

Android 状态栏颜色兼容方案-程序员宅基地

文章浏览阅读80次。2019独角兽企业重金招聘Python工程师标准>>> ..._android 修改状态栏颜色兼容方案

unity3d 中让JS访问C#脚本的方法_unity2018 c# js-程序员宅基地

文章浏览阅读2.8k次。由于是完全不同的两种语言,所以“语言不通”也是很正常的事情。但最终编译后他们都是一样的,所以虽然做不到相互访问(虽然不知道访问这个说法对不对不过这种细节就不要在意了),但是单向访问还是没问题的。网上很多中说法,官方其实帮助文档也有说了,不过是全英文的,国内也有翻译,不过那个质量就不评论了。然后进入正题:1、把C#脚本都放到Standard Assets文件夹里,如果没有_unity2018 c# js

实现高度“听话”的多行文本输入框_input 多行固定高度-程序员宅基地

文章浏览阅读497次。实现高度“听话”的多行文本输入框_input 多行固定高度

Flutter 应用启动流程分析_flutter的main.dart启动-程序员宅基地

文章浏览阅读351次。众所周知,任何应用程序的启动都是从main()函数开始的,Flutter也不例外,main.dart文件的main函数开始的,代码如下。void main() => runApp(MyApp());main函数则调用的是runApp函数,源码如下。void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) ..scheduleWarmU_flutter的main.dart启动

EasyAdmin 控制器自定义方法,自定义模板头部_easyadmin修改后台菜单样式-程序员宅基地

文章浏览阅读1.4k次。在对应的方法内加入下面一句:$this->layout && $this->app->view->engine()->layout(false);完整代码类似如下:/** * @NodeAnotation(title="添加") */ public function add2() { //取消模板布局“layout/default”,文件路径“app\admin\view\layout\default.h._easyadmin修改后台菜单样式

推荐文章

热门文章

相关标签