本文转自:http://blog.chinaunix.net/space.php?uid=22566367&do=blog&id=2747207

在物理页面管理上实现了基于区的伙伴系统(zone based buddy system)。对不同区的内存使用单独的伙伴系统(buddy system)管理,而且独立地监控空闲页。相应接口alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。

补充知识:

1.原理说明

  Linux内核中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表。

  * 页全局目录(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.几种分配函数的比较

  分配原理最大内存其他

  __get_free_pages直接对页框进行操作4MB适用于分配较大量的连续物理内存

  kmem_cache_alloc基于slab机制实现128KB适合需要频繁申请释放相同大小内存块时使用

  kmalloc基于kmem_cache_alloc实现128KB最常见的分配方式,需要小于页框大小的内存时可以使用

  vmalloc建立非连续物理内存到虚拟地址的映射物理不连续,适合需要大内存,但是对地址连续性没有要求的场合

  dma_alloc_coherent基于__alloc_pages实现4MB适用于DMA操 作

  ioremap实现已知物理地址到虚拟地址的映射适用于物理地址已知的场合,如设备驱动

  alloc_bootmem在启动kernel时,预留一段内存,内核看不见小于物理内存大小,内存管理要求较高

转载于:https://www.cnblogs.com/huanonly220/archive/2011/12/30/2307577.html

通过伙伴系统申请内核内存的函数有哪些?相关推荐

  1. linux内核函数kmalloc,Linux内核内存分配函数之devm_kmalloc和devm_kzalloc

    本文介绍Linux内核内存分配函数devm_kmalloc()和devm_kzalloc(). 一.devm_kmalloc 文件:drivers/base/devres.c,定义如下: /** * ...

  2. linux内核函数kmalloc,Linux_Linux平台上几个常见内核内存分配函数,* kmallocPrototype:#incl - phpStudy...

    Linux平台上几个常见内核内存分配函数 * kmalloc Prototype: #include void *kmalloc(size_t size, int flags); Kmalloc分配一 ...

  3. linux 内核 内存申请函数 kmalloc、kzalloc、vmalloc 区别

    我们都知道在用户空间动态申请内存用的函数是 malloc(),这个函数在各种操作系统上的使用是一致的,对应的用户空间内存释放函数是 free().注意:动态申请的内存使用完后必须要释放,否则会造成内存 ...

  4. linux内核内存管理slub

    一.概述 linux内存管理核心是伙伴系统,slab,slub,slob是基于伙伴系统之上提供api,用于内核内存分配释放管理,适用于小内存(小于1页)分配与释放,当然大于1页,也是可以的.小于一页的 ...

  5. [内核内存] [arm64] 内存规整1---memory-compaction详解

    文章目录 1.memory-compaction简介 2.memory-compaction调用流程 3.memory-compaction源码分析 3.1内存规整关键数据结构 3.2struct z ...

  6. Linux内核中常见内存分配函数

    1.      原理说明 Linux内核中采用了一种同时适用于32位和64位系统的内存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系统中,用到了四级页表,如图2-1所示.四级页表分 ...

  7. Linux内核中内存分配函数

    1.原理说明 Linux内核 中采 用了一种同时适用于32位和64位系统的内 存分页模型,对于32位系统来说,两级页表足够用了,而在x86_64系 统中,用到了四级页表,如图2-1所示.四级页表分别为 ...

  8. [内核内存] 伙伴系统4---alloc_pages(内存块分配)

    文章目录 alloc_pages源码流程分析 alloc_pages函数实现流程 代码细节分析 参数准备 核心函数__alloc_pages_nodemask 快速内存分配(get_page_from ...

  9. linux内核内存slab,伙伴系统,内存碎片,内存耗尽(OOM)杀手,内存资源控制器memcg,KASAN学习笔记

    目录 1 基础知识 1.1 页 1.2 页表 1.3 UMA(一致性访问) / NUMA(非一致性访问) 1.4 高端内存和低端内存 1.5 内存模型 2 物理内存的管理 2.1 物理内存的组织:节点 ...

最新文章

  1. Oracle 10g RAC修改IP/VIP地址示例
  2. acwing算法题--直方图中最大的矩形
  3. 英雄传说服务器维护中,英雄传说:星之轨迹 正统《轨迹》手游无法连接服务器是什么原因...
  4. 牛客网暑期ACM多校训练营(第三场)A - PAXM Team(01背包)
  5. leetcode 278. 第一个错误的版本(二分)
  6. 联想m7400pro更换墨粉盒怎么清零_佳能打印机怎么换墨水 佳能打印机换墨水注意事项【详解】...
  7. Swift中文教程(十五) 析构
  8. 如何使用Bootstrap Modal和jQuery AJAX创建登录功能
  9. android canvas自适应屏幕,html5 Canvas 如何自适应屏幕大小?
  10. sklearn 3.随机森林(菜菜课程)
  11. [JNI] 开发基础(4)函数指针及指针函数
  12. windows打流工具IxChariot使用教程
  13. Java 正则表达式的用法与实例
  14. 美国低速自动驾驶在公共交通应用详解 | 自动驾驶系列
  15. 家庭库存管理系统Homebox
  16. 学前端要多久?学前端要多久?学前端多少钱
  17. ImageJ工具的使用
  18. 基于经度坐标校正鱼眼图像---python实现
  19. 1.19.5.4.流上的Join、常规Join、时间区间Join、时态表Join、基于处理时间的时态Join、时态表函数Join、用法
  20. 当B站“不再二次元”,破圈易,盈利难!

热门文章

  1. java用cookie最新浏览商品_jQuery.cookie.js实现记录最近浏览过的商品功能示例
  2. java 常用 函数_java在线报表中有哪些常用函数
  3. Linux命令每五分钟执行一次,Linux crontab 每5秒钟执行一次 shell 脚本 的方法
  4. android xml图片旋转,如何在Android中进行平滑的图像旋转?
  5. excel乘法公式怎么输入_python吊打Excel?那是你不会用!
  6. 广义表的长度和深度怎么算_最新详细个人所得税税率表!快看最新个人所得税怎么算!...
  7. linux voip客户端,linux搭建VOIP
  8. java利用redis实现排行榜_Java简单使用redis-zset实现排行榜
  9. matlab 放射治疗,dicom-rt解析及在精确放射治疗计划系统中的应用.pdf
  10. C语言学习笔记---嵌套结构体