- Linux的页框管理采用什么算法?简述该算法。
Linux的页框管理采用buddy算法(伙伴算法)
把所有空闲页框分组为10(Linux2.6.26为11)个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256和512个连续的页框
每个块的第一个页框的物理地址是该块大小的整数倍
例如:大小为16个页框的块,其起址是16×4KB的倍数
例如:0和1是伙伴,1和2不是伙伴
两个伙伴的大小必须相同,物理地址必须连续
假定伙伴的大小为b
那么第一个伙伴的物理地址必须是2×b×4KB对齐
事实上伙伴是通过对大块的物理内存划分获得的
假如从第0个页面开始到第3个页面结束的内存
每次都对半划分,那么第一次划分获得大小为2页的伙伴
进一步划分,可以获得大小为1页的伙伴,例如0和1,2和3
当两个伙伴都为空闲的时候,就合并成一个更大的块
该过程将一直进行,直到找不到可以合并的伙伴为止
寻找伙伴
给定一个要释放的空闲块
找到其伙伴
查看其状态:合并 or 不合并
假设有128MB的ram。
128MB最多可以分成215=32768个页框,214=16384个8KB(2页)的块或213=8192个16KB(4页)的块,直至64个大小为512个页的块
假设要请求一个大小为128个页框的块(0.5MB)。
算法先free_area[7]中检查是否有空闲块(块大小为128个页框)
若没有,就到free_area[8]中找一个空闲块(块大小为256个页框)
若存在这样的块,内核就把256个页框分成两等份,一半用作满足请求,另一半插入free_area[7]中
如果在free_area[8]中也没有空闲块,就继续找free_area[9]中是否有空闲块。
若有,先将512分成伙伴,一个插入free_area[8]中,另一个进一步划分成伙伴,取其一插入free_area[7]中,另一个分配出去
如果free_area[9]也没有空闲块,内存不够,返回一个错误信号- Linux中的slab算法的用途是什么?简述该算法。
slab算法是对页框管理伙伴系统的改进,用于对内存区的管理。
伙伴系统的分配方法有许多值得改进的地方:
不同的数据类型用不同的方法分配内存可能提高效率。比如需要初始化的数据结构,释放后可以暂存着,再分配时就不必初始化了
内核的函数常常重复地使用同一类型的内存区,缓存最近释放的对象可以加速分配和释放
对内存的请求可以按照请求频率来分类,频繁使用的类型使用专门的缓存,很少使用的可以使用通用缓存
使用2的幂次大小的内存区域时硬件高速缓存冲突的概率较大,有可能通过仔细安排内存区域的起始地址来减少硬件高速缓存冲突
缓存一定数量的对象可以减少对buddy系统的调用,从而节省时间并减少由此引起的硬件高速缓存污染
slab分配器体现了这些改进思想:
slab分配器把内存区看成对象
slab分配器把对象分组放进高速缓存。每个高速缓存都是同种类型内存对象的一种“储备”
例如当一个文件被打开时,存放相应“打开文件”对象所需的内存是从一个叫做filp(file pointer)的slab分配器的高速缓存中得到的,
也就是说每种对象类型对应一个高速缓存。
每个高速缓存被分成多个slabs,每个slab由一个或多个连续的页框组成,其中包含一定数目的对象
每个slab有三种状态:全满,半满,全空
全满意味着slab中的对象全部已被分配出去
全空意味着slab中的对象全部是可用的
半满介于两者之间
当内核函数需要一个新的对象时,
优先从半满的slab满足这个请求
否则从全空的slab中取一个对象满足请求
如果没有空的slab则向buddy系统申请页面生成一个新的slab
slab分配器提供的接口:
创建专用高速缓存:kmem_cache_create
撤销专用高速缓存:kmem_cache_destroy
一般内核撤销一个模块时会调用这个函数撤销属于那个模块的cache类型
从专用高速缓冲中分配和释放
从高速缓存中分配/释放一个内存对象kmem_cache_alloc/kmem_cache_free
从普通高速缓存中分配和释放kmalloc/kfree- 什么是非连续内存区?
把线性空间映射到一组连续的页框是很好的选择,有时候不得不将线性空间映射到一组不连续的页框,这就是非连续内存区。
它的主要优点是避免碎片的产生。
非连续存储区的描述符vm_struct
Vmalloc等分配一个非连续存储区
Vfree释放非连续线性区间- 什么是线性区?列举4种最常见的线性区。
当用户态进程请求动态内存时,并没有立即获得实际的物理页框,而仅仅获得对一个新的线性地址区间的使用权
这个线性地址区间会成为进程地址空间的一部分,称作线性区(memory areas)
进程最多能访问4GB的线性地址空间
但进程在访问某个线性空间之前,必须获得该线性空间的许可
因此,一个进程的地址空间是由允许该进程访问的全部线性地址组成
内核使用线性区资源来表示线性地址空间
每个线性区由起始线性地址、长度和一些存取权限描述
线性区的开始和结束都必须4KB对齐
进程获得新线性区的一些典型情况:
刚刚创建的新进程
使用exec系统调用装载一个新的程序运行
将一个文件(或部分)映射到进程地址空间中
当用户堆栈不够用的时候,扩展堆栈对应的线性区
每个线性区由一个vm_area_struct结构来表示
这个结构描述了一段给定的内存区间
区间中的地址都有同样的属性,比如同样的存取权限和相关的操作函数
用这个结构可以表示各种线性区,比如映射可执行的二进制代码的线形区、用作用户态堆栈的线形区等等
常见的线性区有堆,栈,代码段,数据段,全局变量存储区,文件影射等。- Linux如何描述进程的地址空间?
Linux使用线性区来描述进程的地址空间。
进程最多能访问4GB的线性地址空间
但进程在访问某个线性空间之前,必须获得该线性空间的许可
因此,一个进程的地址空间是由允许该进程访问的全部线性地址组成
内核使用线性区资源来表示线性地址空间
每个线性区由起始线性地址、长度和一些存取权限描述