Linux kernel之二内存管理之连续内存page frame管理

Linux kernel之二内存管理之连续内存page frame管理,第1张

1)虚拟地址空间划分:低3GB 空间用于用户空间,高1GB 用于内核地址空间

2)ZONE DMA 和ZONE_NORMAL 对应内核虚拟地址物理内存直接映射区,其虚拟地址到物理地址映射的PTE 已经建好了,虚拟地址与物理地址之间只相差(PAGE_OFFSET,即0xC0000000)

3)来自ZONE_HIGH 分配的page, 内核没有进行地址映射,需调用者在内核虚拟地址 Fixmaps 区或者动态映射区分配一个虚拟地址,然后映射到该物理page frame 上

处理器通过地址访问内存单元,程序中用到的基址加偏移地址是线性地址,需要通过MMU将虚拟地址映射成物理地址。这给分配和释放内存带来方便:1)物理地址不连续的空间可以映射为逻辑上连续的虚拟地址。2)进程可以获得比实际内存大的"空间",虚拟内存使得进程在这种情况下仍可正常运行。

linux内核为驱动程序提供了一致的内存管理接口,因此不用考虑不同体系结构如何管理内存的。

在linux内核中分配内存用kmalloc和kfree。

kmalloc分配时可以被阻塞,且不对所获得的区域清零。它分配的区域在物理内存中也是连续的。

原型:

#include<linux/slab.h>

void *kmalloc(size_t size,int flags)//参数为分配大小及分配标志

flags参数:

GFP_KERNEL:内核内存通用分配方法,表示内存分配是由运行在内核空间的进程执行的。可休眠,所以使用GFP_KERNEL分配内存的函数必须是可重入的。

GFP_ATOMIC:用于在中断处理例程或者运行在进程上下文之外的代码中分配内存,不可休眠。内核通常会为原子性的分配预留一些空闲页面。

所有标志定义在 <linux/gfp.h>中。

size参数:

内核是基于页技术分配内存,以最佳的利用系统的RAM。

linux处理内存分配的方法是:创建一系列的内存对象池,每个池的内存大小事固定的,处理分配请求时,就直接在包含足够大的内存块中传递一个整款给请求者。内核只能分配一些预定义的固定大小的字节数组。kmalloc能处理的的最小内存块是32或者64,不大于128KB。

内存区段:

linux内核把内存分为3个区段:可用于DMA的内存,常规内存以及高端内存。kmalloc不能分配高端内存。内存区段在mm/page_alloc.c中实现。区段的初始化在对应的arch树下的mm/init.c中。

后备高速缓存 (lookaside cache)

内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间,因此不应该将内存释放回一个全局的内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个互斥锁,那么只需在为互斥锁首次分配内存时执行一次互斥锁初始化函数(mutex_init)即可。后续的内存分配不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。

linux2.6中USB和SCSI驱动程序使用了这种高速缓存,是为一些反复使用的块增加某些特殊的内存池。后背高速缓存管理也叫slab分配器,相关函数和类型在<linux/slab.h>中申明。

slab分配器实现高速缓存具有kmem_cache_t类型。

kmem_cache_t * kmem_cache_create( const char *name, size_t size, size_t align,

unsigned long flags

void (*constructor)(void*,kmem_cache_t *, unsigned long),

void (*destructor)(void*, kmem_cache_t *, unsigned long))

用于创建一个新的高速缓存对象。

constructor用于初始化新分配的对象,destructor用于清除对象。

一旦某个对象的高速缓存被创建以后,就可以调用kmem_cache_alloc从中分配内存对象。

void * kmem_cache_alloc(kmem_cache_t *cache,int flags)

释放内存对象使用kmem_cache_free

void kmem_cache_free(kmem_cache_t *cache,const void *obj)

在内存空间都被释放后,模块被卸载前,驱动程序应当释放他的高速缓存。

int kmem_cache_destory(kmem_cache_t *cache)

要检查其返回状态,如果失败,表明莫块中发生了内存泄露。

基于slab的高速缓存scullc

kmem_cache_t *scullc_cache

scullc_cache=kmem_cache_creat("scullc",scullc_quantum,0,SLAB_HWCACHE_ALIGN,NULL,NULL)

if(!scullc_cache)

{

scullc_cleanup()

return -ENOMEM

}

if(!dpte->data[s_pos])

{

dptr->data[s_pos]=kmem_cache_alloc(scullc_cache,GFP_KERNEL)

if(!dptr->data[s_pos])

goto nomem

memset(dptr->data[s_pos],0,scullc_quantum)

}

for(i=0i<qseti++)

{

if(dptr->data[i])

kmem_cache_free(scullc_cache,dptr->data[i])

}

if(scullc_cache)

kmem_cache_destory(scullc_cache)

内存池:

内核中有些地方的内存分配是不允许失败的,为确保能分配成功,内核建立一种称为内存池的抽象,他试图始终保持空闲状态,以便紧急情况使用。

mempool_t * mempool_creat(int min_nr,

mempool_alloc_t *alloc_fn, //对象分分配 mempool_alloc_slab

mempool_free_t *free_fn, //释放 mempool_free_slab

void *pool_data)

可以用如下代码来构造内存池

cache=kmem_cache_creat(...)//创建一个高速缓存

pool=mempool_creat(MY_POOL_MINIMUM,mempool_alloc_slab,mempool_free_slab,cache)//建立内存池对象

void *mempool_alloc(mempool_t *poll,int gfp_mask)//分配对象

void *mempool_free(void *element,mempool_t *poll)//释放对象

void mempool_destroy(mempool_t *poll)//销毁内存池

注意:mempool会分配一些内存块,空闲且不会被用到,造成内存的大量浪费。所以一般情况不要用内存池。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/yw/7203675.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-04-02
下一篇2023-04-02

发表评论

登录后才能评论

评论列表(0条)

    保存