Linux 伙伴堆算法

声明:本文为引用他人文章,具体来自哪里不记得了,之前收藏在笔记中的

1、为了便于数据的频繁分配与回收,编程人员通常会用到空闲链表。

空闲链表包含可供使用的、已分配好的数据结构块。当代码需要一个新的数据结构实例时,就从空闲链表中抓取一个,而不需要分配内存,把数据放进去。以后,当不再需要这个数据结构时,就把他放回空闲链表中,而不是释放它。

从这个意义上说,空闲链表相当于高速缓存----快速存储频繁使用的对象类型。


2、但是,在内核中,空闲链表面临的一个问题是不能全局控制。当内存紧缺时,内核无法通知每个空闲链表,让它们挤出一些内存空闲来。事实上,内核根本就不知道有这些空闲链表的存在。

所以,为了弥补这一点,Linux内核提供了slab层(也就是所谓的slab分配器)slab分配器扮演了通用的数据结构缓存层的角色。


3、slab规则




频繁使用的数据结构也会频繁分配和释放,因此应当缓存它们。

频繁分配和回收必然会导致内存碎片(难以找到大块的可用内存)。为了避免这种现象,空闲链表的缓存会连续的存放。因为已经释放的数据结构又会放回空闲链表,因此这样就不会导致内存碎片。

slab:每个slab块都是页面大小的整数倍(有上限)

为了便于数据的频繁分配和回收,Linux内核提供了slab层(也就是所谓的slab分配器)。slab分配器扮演了通用数据结构缓存层的角色。

slab层把不同的对象划分为高速缓存,其中每个高速缓存组中存放的都是不同类型的数据结构对象。例如,一个高速缓存用于存放进程描述符,另一个高速缓存用于存放i节点。这些高速缓存又被划分为slab,slab由一个或多个物理上连续的页组成。一般情况下,slab也就仅仅由一页组成。每个高速缓存可以由多个slab组成。

每个slab都包含一些对象成员,这里的对象指的是被缓存的数据结构。每个slab处于三种状态之一:满、部分满或空。当内核的某一部分需要一个对象时,就要由slab分配了,首先考虑的是部分满的slab,如果不存在部分满的slab则去空的slab分配,如果也不存在空的slab,则内核需要申请页重新分配高速缓存。下图描述了高速缓存、slab及对象之间的关系

5、slab与传统内存管理模式比较

与传统的内存管理模式相比, slab 缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab 缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab 分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab 分配器还可以支持硬件缓存对齐和着色,这防止错误的共享(两个或两个对象尽管位于不同的内存地址,但映射到相同的告诉缓冲行),这可以提高性能,但以增加内存浪费为代价。



6、在栈上的静态分配

内核栈大小固定。我们在进程时要注意节省栈资源,要控制函数内的局部变量,尽量不要出现大型数组或大型结构体。尤其对于内核栈,一旦造成溢出,就会影响到内核数据(如thread_info)。所以应当优先考虑动态分配。另外一个进程的内核栈和中断栈是分开的,这样可以减轻内核栈的负担(一个内核栈只占1页或2页)。



7、高端内存的映射

因为32位的处理器能够寻址达到4GB。一旦这些页被分配,就必须映射到内核的虚拟内存空间上。

高于896MB的所有物理内存的范围大都是高端内存,它不会永久或自动的映射到内核虚拟地址空间。

内核地址的虚拟内存大小为1G,其中0-896M的内存与物理内存一一映射,即线性映射。而896MB~1024MB的虚拟内存如果也与物理内存线性映射,那么内核态只能使用1G的物理内存,即使物理内存大于1G(比如4G),这样的话就没有充分利用物理内存了。所以内核虚拟内存中的896MB~1024MB与高端内存不会一一映射。具体的映射方式如下:

当内核态需要访问高端物理内存时,在内核虚拟内存空间中的896-1024MB找一段相应大小空闲的逻辑地址空间,借用一会。借用这段逻辑地址空间,建立映射到想要访问的那段物理内存,临时用一会,用完后归还。这样当进程后面又需要访问其他的高端物理内存时,仍然可以用这段逻辑地址空间。

高端内存的最基本思想:在内核虚拟空间896MB~1024MB的内存中借一段地址空间,建立与高端物理内存的临时地址映射,用完后释放虚拟空间,达到这段虚拟地址空间可以循环使用,访问所有物理内存。

高端内存映射有三种方式:

1、映射到“内核动态映射空间”

这种方式很简单,因为通过 vmalloc() ,在”内核动态映射空间“申请内存的时候,就可能从高端内存获得页面(参看 vmalloc 的实现),因此说高端内存有可能映射到”内核动态映射空间“ 中。
2、永久内核映射
如果是通过alloc_page() 获得了高端内存对应的 page,如何给它找个线性空间?
内核专门为此留出一块线性空间,从 PKMAP_BASE 到 FIXADDR_START ,用于映射高端内存。在 2.4 内核上,这个地址范围是 4G-8M 到 4G-4M 之间。这个空间起叫“内核永久映射空间”或者“永久内核映射空间”。这个空间和其它空间使用同样的页目录表,对于内核来说,就是 swapper_pg_dir,对普通进程来说,通过 CR3 寄存器指向。通常情况下,这个空间是 4M 大小,因此仅仅需要一个页表即可,内核通过来 pkmap_page_table 寻找这个页表。
3、临时映射

当必须创建一个映射而当前的上下文又不能睡眠时,内核提供了临时映射(也就是原子映射)。有一组保留的映射,他们可以存放新创建的临时映射。内核可以原子地把高端内存中的一个页映射到某个保留的映射中。因此,临时映射可以用在不能睡眠的地方,比如中断处理程序中,因为获取映射时绝不会阻塞。


8、每个CPU数据

SMP环境下加锁过多的话,会严重影响并行的效率,如果是自旋锁的话,还会浪费其他CPU的执行时间。所以内核中才有了按CPU分配数据的接口。按CPU分配数据之后,每个CPU自己的数据不会被其他CPU访问,虽然浪费了一点内存,但是会使系统更加的简洁高效。

按CPU来分配数据主要有2个优点:

1.最直接的效果就是减少了对数据的锁,提高了系统的性能

2.由于每个CPU有自己的数据,所以处理器切换时可以大大减少缓存失效的几率。因为如果一个处理器操作某个数据,而这个数据在另一个处理器的缓存中时,那么存放这个数据的那个处理器必须清理或刷新自己的缓存。持续的缓存失效成为缓存抖动,对系统性能影响很大。


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值