本文是本人在网上学习后总结出来的,有不对的地方请多多指教。
推荐的相关文章:
https://xiaolincoding.com/os/3_memory/vmem.html
https://zhuanlan.zhihu.com/p/149581303
内存
两种"内存"
系统中内存一般分为两种:
- 物理内存 – 存放数据
- 虚拟内存 – 管理物理内存
他们有什么关系
虚拟内存的地址与物理内存的地址有映射关系
大概转换的关系为: 虚拟(逻辑)地址->分段->线性地址->分页->物理地址
这个过程通过MMU完成的
有物理内存不就行了吗?虚拟内存能干嘛
如果只有一个进程时,直接跑在物理内存确实没有问题,但是如果要实现多进程的话,可能就需要很大的内存,或者会出现一个进程的内存覆盖到另外一个进程.
总的说就是有两个问题:
- 跑不了多进程
- 用户无法保证进程之间不会互相误操作,导致系统崩溃
虚拟内存的作用可以说很好的防止了这两种情况的发生
- 一种是针对进程的管理
- 一种是针对于物理内存
针对进程的内存管理
对于进程的管理,它会给每一个进程分配相同的地址空间,大小为4GB(32位系统),这4G又分为1G的内核空间,3G用户空间.
- 用户空间:程序(代码)段,数据段,BSS段,堆,(文件映射段),栈,其中堆向上生长,栈向下.用户空间是每一个进程独有的
- 内核空间:直接映射区,固定内存映射区,永久内存映射区,动态映射区;
- 直接映射区是对应着物理内存的前896MB,正好是ZONE_DMA+ZONE_NORMAL
- 动态内存映射区是用kmalloc()或者vmalloc()分配的
- 永久内存映射区可访问高端内存(ZONE_HIGHMEM)
- 固定内存映射区每个地址项用于服务特定的用途
虽然说系统分配给每个进程的空间都是虚拟且独立互不干扰的,但是内核空间都是映射到物理内存的,大家都是同一块.所以这一块涉及到具体的进程管理,咱们后续在说.
针对物理内存
前面提过MMU会转换内存的大概,具体的内存转换关系就要提到分页和分段了,这是内存管理的两种方式
- 分段:把程序分成5个段(代码,数据,BSS,堆,栈),通过不同属性存储到不同地方,虚拟地址会包含段选择因子和偏移量,用于索引;
- 分页:将内存分配成固定大小的页(4KB),程序被分成若干页,每一页又被页表存储相应的索引,页表又存储在内存;
对于这两种机制都是为了很好的去抑制内存碎片的产生,虽然还是会产生内存碎片.
- 内部内存碎片: 一个页框内的内存碎片是内部碎片,毕竟对于很小的内存来说,系统也会为它分配一页的大小
- 外部内存碎片: 多个页框间的碎片是外部碎片,页面的频繁分配与释放会导致大量的分散页面群夹杂在已分配页面中间
Swap
对上面的情况,很容易产生内存不够的情况,对于这种情况系统会采用Swap机制,它会将最近没被使用的内存页面给释放掉,也就是暂时写在硬盘上,一旦需要的时候,再加载进来.
可以理解为这种交换机制使得进程对运行内存超过物理内存大小,使得多进程成为可能.
不过不能太过依赖Swap机制,不然内存严重不足的时候,频繁的交换会使整个机器很卡.
两者结合起来,那么就有了段页式,它既有段表也有页表,就是将程序先分段,在段表中不同段对应不同的段页表,段页表又对应不同的物理页号,物理页号又可以加位移得到物理地址,不过即便这样也无法根除内存碎片的存在.
有什么好的办法呢?
目前的话介绍两种算法,即伙伴分配算法和slab算法:
伙伴分配算法
- 概念: 什么意思呢?其实就是把相同大小的页框块链起来,每个页框块就像手牵手的好伙伴;具体内容是所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512以及1024个连续页框大小的页框块,最大可以申请1024个连续的页框内存(4MB);这样就保证任何大小的整数页框大小的的程序都可以被各种组合表示.
- 优点: 有效减少了外部碎片产生
-> 1 -> 1 -> 1 -> 1 |
---|
-> 2 -> 2 -> 2 -> 2 |
-> 4 -> 4 -> 4 -> 4 |
… |
-> 1024 -> 1024 -> 1024 -> 1024 |
举个例子,我需要申请5个页框,那么我需要一个但是长度为4的一个页框和一个长度为1的页框,但是长度为4的连续页框块链表没有空闲的页框块,那么伙伴系统会就会从长度为8的页框块的链表获取一个,并将其拆分为两个长度为4页框,取其中一个,另外一个放入长度为4页框块的空闲链表中.页框释放的时候就检查,释放的这几个页框前后的页框是否空闲,能否组成下一级长度的块. |
slab分配器
- 概念: 内核中有大量的小对象,比如文件描述结构对象,任务描述结构对象,如果按照伙伴系统按页分配和释放内存,对小对象频繁的执行分配 内存-初始化-释放内存 会非常消耗性能.所以就出现了slab,它相当于通过将内存按使用对象不同再划分成不同大小的空间,每当要申请这种类型的对象时,就从缓存池的slab 列表中分配一个出去;而当要释放时,将其重新保存在该列表中
- 优点: 减少了内部碎片的产生,也可以重复利用一些相同的对象,减少内存分配次数.
slab分配器由3种链表来管理
- slabs_full: 完全分配的 slab链表
- slabs_partia: 部分分配的slab链表
- slabs_empty: 没有被分配对象的slab链表
它们之间的关系:
如果都满了,就得请求伙伴系统分页了
以上的这些内容差不多能够理解内存管理的机制了
手动内存分配
用户空间
- malloc
- 当申请小于128KB小内存的时,malloc使用sbrk或brk分配内存;
- 当申请大于128KB的内存时,使用mmap函数申请内存;
内核空间
kmalloc和vmalloc分别用于分配不同映射区的虚拟内存
-
kamlloc
- 分配的虚拟地址范围在内核空间的 直接内存映射区 ;
-
vmalloc
- 分配的虚拟地址区间,位于vmalloc_start与vmalloc_end之间的 动态内存映射区 ;