Linux 内存管理。

首先我先说明最近才真的用心整理和查看linux内存管理方面知识,之所以写这篇博客只是为了整理。

fightting

参考书籍 Linux 内核源代码情景分析
深入理解Linux虚拟内存管理

首先还是上一些关于虚拟内存要问的一些问题吧。

什么是线性地址,逻辑地址,虚拟地址,物理地址
cpu总线发出的地址是什么地址,需要什么过程才能转换为物理地址
什么是内存的分段技术,什么是分页技术,
实模式和保护模式
多级页映射的问题,页目录和页表
虚拟地址空间和虚拟内存的概念
如何获取一个虚拟地址对应的物理地址
32为下的LINUX操作系统虚拟地址映射问题
缺页异常处理do_page_fault
slab机制和伙伴系统
虚拟内存管理和物理内存管理

总共就这莫多问题把


地址概念问题

逻辑地址,逻辑地址就是我们调试和打印的地址,它只是一种偏移地址
线性地址 线性地址就是逻辑地址加上段地址(具体自己查原因(这也是x86体系下的问题(linux下段的地址都是0)))
虚拟地址 保护模式下的段地址+offset
真实内存的地址地址总线上的地址

分段机制

想死啊这是第四次写这个东西了前三次都没有保存。算了继续来吧就当在记一下
  • 分段机制的寻址物理地址=段地址+offset
  • 保护模式下的寻址 物理地址=段描述表的地址+offset 这个也叫线性地址。
    防止越权访问以及越届访问的情况的发生。

先给个大的概念,物理内存是供的关系而虚拟内存是需的关系。举个类似malloc
如果一个进程对虚拟空间的使用都是有限制的,通常都是使用的非连续的部分,比如代码段和数据段 堆栈这些离散的空间,对应的虚拟存储的管理。
首先cpu得到是线性地址,如果要找到实际的物理地址,就要通过多级页映射。具体而言分为4步
虚拟地址最高的那1个字段是在PGD查找当前的表项得到的是中间目录,通过第二个所对应的下标找到对应的页面表,在页面表中找到存放物理页的指针,最后的段是表示offset所以物理页的地址加上对应的偏移就是最终的物理得知。

物理内存页的周转(物理内存管理)

内存页的申请和释放具体这个过程如下
总体流程
1、空闲。
页面page数据结构通过其队列头链入对应管理区空闲队列free_area。页面计数count=0
2、分配
通过函数_alloc_pages()或_get_free_page()从空闲队列分配内存页面,并将所分配页面计数为1,其从free_area队列移除
如果无进行内存交换操作
3、上个页面链入活跃队列active_list,并且至少有一个进程的用户空间指向该页面。每当为其页面再建立或恢复映射时,页面计数count+1;
4、每当断开页面映射,页面计数-1。如果修改了页面内容且页面不再用即count=0,则链入inactive_dirty_list队列。
5、将不活跃脏页面写入交换设备,从inactive_dirty_list移到inactive_clean_list队列。
6、如果之后又收到访问,则又到活跃页面并恢复映射。
7、需要时则从“干净队列”中回收页面。

伙伴算法

参考https://www.cnblogs.com/cherishui/p/4246133.html
伙伴算法的实现原理
为了便于页面的维护,将多个页面组成内存块,每个内存块都有 2 的方幂个页,方幂的指数被称为阶 order。order相同的内存块被组织到一个空闲链表中。伙伴系统基于2的方幂来申请释放内存页。
当申请内存页时,伙伴系统首先检查与申请大小相同的内存块链表中,检看是否有空闲页,如果有就将其分配出去,并将其从链表中删除,否则就检查上一级,即大小为申请大小的2倍的内存块空闲链表,如果该链表有空闲内存,就将其分配出去,同时将剩余的一部分(即未分配出去的一半)加入到下一级空闲链表中;如果这一级仍没有空闲内存;就检查它的上一级,依次类推,直到分配成功或者彻底失败,在成功时还要按照伙伴系统的要求,将未分配的内存块进行划分并加入到相应的空闲内存块链表
在释放内存页时,会检查其伙伴是否也是空闲的,如果是就将它和它的伙伴合并为更大的空闲内存块,该检查会递归进行,直到发现伙伴正在被使用或者已经合并成了最大的内存块。
这里写图片描述
缺点只有对页外的内存碎片就进行解决,并没有对页内的内存碎片进行解决,合并必须在同一个内存块上。

slab算法:

一般来说,伙伴算法的改进算法用于操作系统分配和回收内存,而且内存块的单位较大,利于Linux使用的伙伴算法以页为单位.对于小块内存的分配和回收,伙伴算法就显得有些得不偿失了.
对于小块内存,一般采用slab算法,或者叫做slab机制.
Linux 所使用的 slab 分配器的基础是 Jeff Bonwick 为SunOS 操作系统首次引入的一种算法。Jeff的分配器是围绕对象缓存进行的。在内核中,会为有限的对象集(例如文件描述符和其他常见结构)分配大量内存。Jeff发现对内核中普通对象进行初始化所需的时间超过了对其进行分配和释放所需的时间。因此他的结论是不应该将内存释放回一个全局的内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个互斥锁,那么只需在为互斥锁首次分配内存时执行一次互斥锁初始化函数(mutex_init)即可。后续的内存分配不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。
Linux slab分配器使用了这种思想和其他一些思想来构建一个在空间和时间上都具有高效性的内存分配器。
图中给出了 slab结构的高层组织结构。在最高层是 cache_chain,这是一个 slab 缓存的链接列表。这对于 best-fit算法非常有用,可以用来查找最适合所需要的分配大小的缓存(遍历列表)。cache_chain 的每个元素都是一个 kmem_cache 结构的引用(称为一个 cache)。它定义了一个要管理的给定大小的对象池。
每个缓存都包含了一个 slabs 列表,这是一段连续的内存块(通常都是页面)。存在3 种 slab:
slabs_full:完全分配的slab
slabs_partial:部分分配的slab
slabs_empty:空slab,或者没有对象被分配
slab 列表中的每个 slab都是一个连续的内存块(一个或多个连续页),它们被划分成一个个对象。这些对象是从特定缓存中进行分配和释放的基本元素。注意 slab 是 slab分配器进行操作的最小分配单位,因此如果需要对 slab 进行扩展,这也就是所扩展的最小值。通常来说,每个 slab 被分配为多个对象。
由于对象是从 slab 中进行分配和释放的,因此单个 slab 可以在 slab列表之间进行移动。例如,当一个 slab中的所有对象都被使用完时,就从slabs_partial 列表中移动到 slabs_full 列表中。当一个 slab完全被分配并且有对象被释放后,就从 slabs_full 列表中移动到slabs_partial 列表中。当所有对象都被释放之后,就从 slabs_partial 列表移动到 slabs_empty 列表中。
slab背后的动机
传统的内存管理模式相比, slab缓存分配器提供了很多优点。首先,内核通常依赖于对小对象的分配,它们会在系统生命周期内进行无数次分配。slab缓存分配器通过对类似大小的对象进行缓存而提供这种功能,从而避免了常见的碎片问题。slab分配器还支持通用对象的初始化,从而避免了为同一目而对一个对象重复进行初始化。最后,slab分配器还可以支持硬件缓存对齐和着色,这允许不同缓存中的对象占用相同的缓存行,从而提高缓存的利用率并获得更好的性能。

虚拟内存管理

如果说物理内存管理是对供的管理,那么虚拟内存是对需的管理。
Linux 的虚拟内存管理有几个关键概念:
每个进程有独立的虚拟地址空间,进程访问的虚拟地址并不是真正的物理地址
虚拟地址可通过每个进程上页表与物理地址进行映射,获得真正物理地址
如果虚拟地址对应物理地址不在物理内存中,则产生缺页中断,真正分配物理地址,同时更新进程的页表;如果此时物理内存已耗尽,则根据内存替换算法淘汰部分页面至物理磁盘中。

定期的页面交换策略

最简单的一种情况时,如果访问虚拟页时发现该页不在实际的内存上,或者没有分配到实际物理页。那么就会发生缺页中断。缺页中断做的事就是将磁盘的页放入内存,并产生映射关系。或者分配页。
但是这块却又一个问题,对于物理内存而言不可能随时都有足够的内存页,所以它回选择置换一个长时间没有使用的页换到swap分区,但是如果 每当缺页的时候在去换,使得cpu此刻过于繁忙,所以操作系统会在一隔段时间内选择一些页置换的swap分区上,保证内存中有合适的空闲页。具体算法位LRU 算法但是这却造成了一个很严重的问题。如果当前页刚被置换出去后如果访问要重新从磁盘上想内存中调。这是一个很难受的问题。这个问题也叫做也页面的抖动。

页面抖动的解决,可以将换出和和页面释放分开。

具体而言加入一个缓冲队列(也叫不活跃的队列)。
首先如果该页因为很久没被访问,那么首先他会被放到缓冲队列上,然后将其写入到磁盘,但是并不释放的当前的物理页,只是解除当前的物理页和虚拟页之间的映射。然后等待该页的老化,如果该页被访问,直接从缓冲队列上取走并且放入到活跃的队列上,重新建立起映射关系。如果没被访问等待页的老化和回收。但是这块也有一个很重要的问题,如果当前的物理页在磁盘,如果在物理页中被修改,重新老化释放的话就要写会磁盘,(脏页),如果没有被修改,在缓冲队列放入时候就不会写回磁盘。
(剩下的问题就在这个知乎中)
https://zhuanlan.zhihu.com/p/26137521?utm_source=qq&utm_medium=social

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值