文档章节

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

有些服务器
 有些服务器
发布于 2015/07/14 15:41
字数 2289
阅读 144
收藏 16

1.原理说明

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

l   页全局目录(Page Global Directory)

l   页上级目录(Page Upper Directory)

l   页中间目录(Page Middle Directory)

l   页表(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.mempool_alloc

  void *mempool_alloc(mempool_t *pool,int gfp_mask)

 为了确保在内存分配不允许失败情况下成功分配内存,内核提供了称为内存池( "mempool" )的抽象,它其实是某种后备高速缓存,mempool的底层通常使用slab。它为了紧急情况下的使用。所以使用时必须注意:mempool会分配一些内存块,使其空闲而不真正使用,所以容易消耗大量内存。而且不要使用mempool处理可失败的分配。应避免在驱动代码中使用mempool。

2.4.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.5.vmalloc

void *vmalloc(unsigned long size)



前面几种内存分配方式都是物理连续的,能保证较低的平均访问时间。但是在某些场合中,对内存区的请求不是很频繁,较高的内存访问时间也可以接受,这是就可以分配一段线性连续,物理不连续的地址,带来的好处是一次可以分配较大块的内存。图3-1表示的是vmalloc分配的内存使用的地址范围。vmalloc对一次能分配的内存大小没有明确限制。出于性能考虑,应谨慎使用vmalloc函数。在测试过程中,最大能一次分配1GB的空间。

2.6.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.7.ioremap

void * ioremap (unsigned long offset, unsigned long size)

ioremap是一种更直接的内存“分配”方式,使用时直接指定物理起始地址和需要分配内存的大小,然后将该段物理地址映射到内核地址空间。ioremap用到的物理地址空间都是事先确定的,和上面的几种内存分配方式并不太一样,并不是分配一段新的物理内存。ioremap多用于设备驱动,可以让CPU直接访问外部设备的IO空间。ioremap能映射的内存由原有的物理内存空间决定,所以没有进行测试。

2.8.Boot Memory

如果要分配大量的连续物理内存,上述的分配函数都不能满足,就只能用比较特殊的方式,在Linux内核引导阶段来预留部分内存。

2.8.1. 在内核引导时分配内存

void* alloc_bootmem(unsigned long size)

可以在Linux内核引导过程中绕过伙伴系统来分配大块内存。使用方法是在Linux内核引导时,调用mem_init函数之前用alloc_bootmem函数申请指定大小的内存。如果需要在其他地方调用这块内存,可以将alloc_bootmem返回的内存首地址通过EXPORT_SYMBOL导出,然后就可以使用这块内存了。这种内存分配方式的缺点是,申请内存的代码必须在链接到内核中的代码里才能使用,因此必须重新编译内核,而且内存管理系统看不到这部分内存,需要用户自行管理。测试结果表明,重新编译内核后重启,能够访问引导时分配的内存块。

2.8.2. 通过内核引导参数预留顶部内存

在Linux内核引导时,传入参数“mem=size”保留顶部的内存区间。比如系统有256MB内存,参数“mem=248M”会预留顶部的8MB内存,进入系统后可以调用ioremap(0xF800000,0x800000)来申请这段内存。

© 著作权归作者所有

有些服务器
粉丝 4
博文 49
码字总数 86607
作品 0
南昌
私信 提问
加载中

评论(1)

笨小子
[0]
最近的学习---一句话收获(备查用)(2)

1.在内核中分配大内存问题。 首先要明白内核没有义务为你分配连续的物理内存,因此你需要的内核中连续的物理内存就是苛刻的要求,既然在计算机系统抽象给用户一个连续的美丽的一维的虚拟内存...

晨曦之光
2012/04/10
62
0
不要盲目增加ip_conntrack_max-理解Linux内核内存

1.由ip_conntrack引出的Linux内存映射有很多文章在讨论关于ip_conntrack表爆满之后丢弃数据包的问题,对此研究深入一些的知道Linux有个内核参数ip_conntrack_max,在拥有较大内存的机器中默认...

晨曦之光
2012/04/10
342
0
Linux驱动虚拟地址和物理地址的映射

一般情况下,Linux系统中,进程的4GB内存空间被划分成为两个部分——用户空间和内核空间,大小分别为0~3G,3~4G。 用户进程通常情况下,只能访问用户空间的虚拟地址,不能访问到内核空间。 ...

baidu_37503452
2018/05/23
0
0
Windows xp/Vista/Linux的内核映射以及内存置换算法

linux的内存管理我认为是高效的,windows xp的我认为是豪华的,vista的呢?我认为是无以伦比的(理论是无以伦比,但是实际上不是那样)。 linux把内核的主要数据结构映射到内核空间3g到3g+76...

晨曦之光
2012/04/10
319
0
Linux下几款C++程序中的内存泄露检查工具

Linux下几款C++程序中的内存泄露检查工具 Linux下编写C或者C++程序,有很多工具,但是主要编译器仍然是gcc和g++。最近用到STL中的List编程,为了检测写的代码是否会发现内存泄漏,了解了一下...

优惠券发放
2018/08/25
0
0

没有更多内容

加载失败,请刷新页面

加载更多

G1 垃圾收集器介绍-转

https://www.cnblogs.com/ASPNET2008/p/6496481.html

Java搬砖工程师
20分钟前
0
0
超高性能 key-value 数据库 Redis-基础数据结构

Redis的魅力 缓存大致可以分为两类:1.一种是应用内缓存,比如Map(简单的数据结构),以及EH Cache(Java第三方库);2.另一种 就是缓存组件,比如Memached,Redis;Redis(remote dictiona...

须臾之余
31分钟前
3
0
Mysql表分区的选择与实践小结

在一些系统中有时某张表会出现百万或者千万的数据量,尽管其中使用了索引,查询速度也不一定会很快。这时候可能就需要通过分库,分表,分区来解决这些性能瓶颈。 一. 选择合适的解决方法 1....

小谜弟
37分钟前
3
0
为 git 添加多个公秘钥

如果想为主机配置多个git设置,设置多个git公、秘钥,只需在生成密钥时指定密钥保持的文件即可,保证保存密钥的文件不同即可。 示例: ssh-keygen -t rsa -C "YOUR_EMAIL@YOUREMAIL.COM" -f...

niithub
37分钟前
1
0
walle-web 2.0安装流水

一、环境安装 VMware Workstation,centos7.6 64位,lnmp1.5 二、安装lnmp1.5 wget http://soft.vpser.net/lnmp/lnmp1.5.tar.gz -cO lnmp1.5.tar.gz && tar zxf lnmp1.5.tar.gz && cd lnmp1......

我心中有猛狗
39分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部