linux物理内存的分配与回收,物理内存分配与回收

我们知道对于物理内存的分配与回收,是先通过内存页面的管理,首先在虚存空间中分配一个虚存区间,然后根据需要为此区间建立起相应的映射并分配对应的物理页面。由于linux操作系统是多任务、多用户的,所以会有很多用户程序的执行与结束,如此频繁的进行内存分配与释放,势必造成许多小块闲散空间的产生,如何能利用起来?

linux采用了伙伴算法来解决问题。伙伴算法的主要思想是把所有闲散的页面分为10块用链表链接起来,每个链表的块中还有2的幂次方的页面。当我们需要一定数量的页面时,先从对应的链表中查找,如果存在就分配,不存載就继续向高一块查找是否有空闲,有的话分配出来,然后将剩余的空间插入到下面相应的链表。

物理页块的分配是通过函数__get_free_pages实现的。

unsigned

long __get_free_pages(gfp_t gfp_mask, unsigned int order)

{

struct

page *page;

VM_BUG_ON((gfp_mask

& __GFP_HIGHMEM) != 0);

page

= alloc_pages(gfp_mask, order);

if

(!page)

return

0;

return

(unsigned long) page_address(page);

}

其参数gfp_mask:表示所分配内存的特殊要求。常用的标志为GFP_KERNEL和GFP_ATOMIC,前者表示在分配内存期间可以睡眠,用于进程;后者表示不可以睡眠,用于中断处理程序。__get_free_pages()返回值是个32位的地址。从上面函数可以看出来物理页面的分配实际上是通过alloc_pages进行分配完成的。

除了分配内存之外,还要确保剩余内存足以应对紧急情况的处理。

页块分配出去用完后要进行回收,linux使用free_pages进行页块回收。

void

free_pages(unsigned long addr, unsigned int order)

{

if

(addr != 0) {

VM_BUG_ON(!virt_addr_valid((void

*)addr));

__free_pages(virt_to_page((void

*)addr), order);

}

}

其中VM_BUG_ON宏其实就是一个循环操作,宏定义如下:

#define

VM_BUG_ON(cond) do { (void)(cond); } while (0)

而virt_addr_valid则是对地址进行一系列判断,宏定义如下:

#define

virt_addr_valid(kaddr) (((void *)(kaddr) >= (void

*)PAGE_OFFSET) && ((void *)(kaddr) < (void*)memory_end))

当然真正其回收作用的就是__

__free_pages函数。

自此我们知道伙伴算法分配内存时,每次至少分配一个页面,当我们需要的内存少于一个页面时,或者更小的数据时,该如何做?Linux引入了slab分配模式。

slab的主要思想是对内核数据进行页面分配时,首先要对数据结构进行初始化,用完之后就要回收。这样不就在重复初始化上花费了很多时间,为了避免这种情况,slab并不会丢弃已分配的对象,而是释放后依然把他们保留在缓冲区中,以便以后请求分配同一对象时,就可以快速获得而免去了初始化。

slab缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。

Slab的构成如下图:

您没有插入代码!

linux把缓冲区分为专用和通用,其中专用缓冲区主要用于频繁使用的数据结构,而通用缓冲区就主要用于开销不大的数据结构了。

Slab的API主要如下:

专用缓冲区:

缓冲区的创建通过kmem_cache_create()建立,其原型如下:

struct

kmem_cache *kmem_cache_create(

const char *name, size_t size, size_t offset,

unsigned long c_flags;

void (*ctor)(void*, struct kmem_cache *, unsigned long),

void (*dtor)(void*, struct kmem_cache *, unsigned long));

其中name为缓冲区的名字,size为对象的大小,offset参数定义了每个对象必需的对齐。c_flags参数指定了为缓存启用的选项。其可能取值SLAB_HWCACHE_ALIGN表示与第一个缓冲区中的缓冲行边界对其;SLAB_NO_REAP表示允许系统回收内存;SLAB_CACHE_DMA表示使用的是DMA内存(DMA是直接存储器访问的缩写,他允许不同速度的硬件装置来沟通,而不需要依于CPU的大量 中断 负载);最后两个函数分别是构造函数(用于对数据初始化)和析构函数(用于对数据回收处理)。其中数据结构kmem_cache是用来对缓冲区进行管理的。其数据结构如下:

struct

kmem_cache {

53

/* 1) per-cpu data, touched during every alloc/free */

54

struct array_cache *array[NR_CPUS];

55

/* 2) Cache tunables. Protected by cache_chain_mutex */

56

unsigned int batchcount;

57

unsigned int limit;

58

unsigned int shared;

59

60

unsigned int buffer_size;

61

u32 reciprocal_buffer_size;

62

/* 3) touched by every alloc & free from the backend */

63

64

unsigned int flags; /* constant flags */

65

unsigned int num; /* # of objs per slab */

…...........

}

缓冲区的分配与释放函数分别如下:

kmem_cache_alloc(struct

kmem_cache *cachep, gfp_t flags);

kmem_cache_free(struct

kmem_cache *cachep, void *objp)

内核函数kmem_cache_destroy用来销毁缓存。这个调用是由内核模块在被卸载时执行的。在调用这个函数时,缓存必须为空。

voidkmem_cache_destroy(

struct kmem_cache *cachep );

通用缓冲区:

通用缓冲区中分配和释放缓冲区的函数为:

void

*kmalloc(size_t,int flags );

void

kfree(const void *objp);

当然还有其他函数来辅助slab完成任务。kmem_cache_size函数会返回这个缓存所管理的对象的大小。您也可以通过调用kmem_cache_name来检索给定缓存的名称(在创建缓存时定义)。具体函数原型如下:

unsigned intkmem_cache_size( struct kmem_cache *cachep );

const char *kmem_cache_name( struct kmem_cache *cachep );

实现上述函数的内核模块#include #include #include #include #include #include #include MODULE_LICENSE("GPL");

static struct kmem_cache *my_cache;

int my_cache_test(void);

static int __init  my_cache_init(void)

{

printk("I am coming.....\n");

my_cache=kmem_cache_create("mycache",32,0,SLAB_HWCACHE_ALIGN,NULL);

if(!my_cache)

{

printk("my_cache_init():cannot create !\n");

}

my_cache_test();

return 0;

}

int my_cache_test(void)

{

void *obj;

//    printk("cache name is %s\n",kmem_cache_name(my_cache));

printk("cache object size is %d\n",kmem_cache_size(my_cache));

obj=kmem_cache_alloc(my_cache,GFP_KERNEL);

if(!obj)

{

printk("alloc error\n");

}

else{

kmem_cache_free(my_cache,obj);

}

return 0;

}

void remove_my_cache(void)

{

if(my_cache)

kmem_cache_destroy(my_cache);

printk("destroy success!\n");

return ;

}

static void __exit my_cache_exit(void)

{

printk("leaving....\n");

kmem_cache_destroy(my_cache);

remove_my_cache();

return;

}

MODULE_LICENSE("GPL");

module_init(my_cache_init);

module_exit(my_cache_exit);

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值