内存管理 - -- - - --

虚拟内存

内存分段

内存分页

段页式内存管理

Linux内存管理

虚拟内存:

单片机的CPU是直接操作内存的物理地址

缺点:无法同时在内存中运行多个程序,因为后来的程序会覆盖之前的程序

问题的关键在于两个程序都使用的是绝对物理地址

如何解决?

 操作系统会提供一种机制,将不同进程的虚拟地址和不同内存的物理地址映射起来

程序在访问虚拟地址的时候,由操作系统转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了

程序所使用的内存地址叫做虚拟内存地址(VMA)

实际存在硬件里面的空间地址叫物理内存地址(PMA)

一张图说明一切

 虚拟地址和物理地址之间的 关系如何管理?

内存分段和内存分页

内存分段:

内存分段:程序是由若干个逻辑分段组成的,如可由代码分段、数据分段、栈段组成。不同的段段有不同的属性,所以就用分段(segmentation)的形式把这些段分开

 段选择子和段内偏移量 组成了分段机制下的虚拟地址

  •  段选择子保存在段寄存器里面。段选择子里面最重要的是段号。用作段表的索引。段号里面保存的是这个段的基地址,段的界限和特权等级等。
  • 虚拟地址中的段内偏移量应该位于0和段界限之间,如果段内偏移量应该位于0和段界限之间,如果段内偏移量是合法的,就将段内偏移量是合法的,就将段基地址加上段内偏移量得到物理内存地址

 分段的缺点:

  • 内存碎片
  • 内u你交换的效率低

为什么会有内存碎片,请看下图:

内存碎片分类:

  • 外部内存碎片:产生了多个不连续的小物理内存,导致新的程序无法被装载
  • 内部内存碎片:程序以段的形式都装入了物理内存,但是这个程序有部分的内存并不常用,也会导致内存的浪费(个人认为在分页那里体现的比较明显)

解决外部碎片的方法是 内存交换

为什么分段会导致内存交换效率低?

如果内存交换的时候,交换的是一个占内存空间很大的程序,这样整个机器就会显得卡顿。

为了解决内存分段的内存碎片和内存交换效率低的问题,就出现了内存分页。

分段的好处:能产生连续的内存空间

内存分页(Paging):

出发点:能少出现一些内存碎片,在进行内存交换的时候,让需要交换写入或者从磁盘装载的数据更少一点

分页是把整个虚拟和物理内存空间切成一段段固定大小的页,通常为4KB的大小

虚拟地址和物理地址之间通过页表来映射

页表存储在内存中,MMU的功能就是将虚拟地址转换为物理地址

当进程访问的虚拟地址在页表中找不到时,系统会产生缺页中断,进入系统内核分配物理内存,更新进程页表,最后返回用户空间,恢复进程的运行

分页如何解决分段的内存碎片、内存交换效率低的问题?

释放的内存都是以页为单位释放的,就不会产生无法给进程使用的小内存(因为小)

内存不够用时,一次性也只是交换内存中很少使用的部分页,效率相对比较高(还是因为页小)

什么时候加载页?在内存运行中,需要用到对应虚拟内存页俩米娜的指令和数据时,再加载到物理内存里面去。

在分页机制下,虚拟地址由页号页内偏移组成。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址,这个基地址与页内偏移的组合就形成了物理内存地址。

 内存地址转换的步骤:

  • 把虚拟内存地址,切分成页号和偏移量
  • 根据页号,从页表里面,查询对应的物理页号
  • 直接拿物理页号,加上前面的偏移量,就得到了物理内存地址

虚拟内存中的页,通过页表映射到物理内存中的页,如下图所示:

 问题:这样设计的话,操作系统中有很多进程的话,页表将会非常大,

 在 32 位的环境下,虚拟地址空间共有 4GB,假设⼀个⻚的⼤⼩是 4KB(2^12),那么就需要⼤约 100 万 (2^20) 个⻚,每个「⻚表项」需要 4 个字节⼤⼩来存储,那么整个 4GB 空间的映射就需要有 4MB 的内存来存储⻚表。 这 4MB ⼤⼩的⻚表,看起来也不是很⼤。但是要知道每个进程都是有⾃⼰的虚拟地址空间的,也就说都有 ⾃⼰的⻚表。 那么, 100 个进程的话,就需要 400MB 的内存来存储⻚表,这是⾮常⼤的内存了,更别说 64 位的环 境了。

如何解决:多级页表

要解决上⾯的问题,就需要采⽤⼀种叫作多级⻚表(Multi-Level Page Table)的解决⽅案。 在前⾯我们知道了,对于单⻚表的实现⽅式,在 32 位和⻚⼤⼩ 4KB 的环境下,⼀个进程的⻚表需要装 下 100 多万个「⻚表项」,并且每个⻚表项是占⽤ 4 字节⼤⼩的,于是相当于每个⻚表需占⽤ 4MB ⼤⼩ 的空间。 我们把这个 100 多万个「⻚表项」的单级⻚表再分⻚,将⻚表(⼀级⻚表)分为 1024 个⻚表(⼆级⻚ 表),每个表(⼆级⻚表)中包含 1024 个「⻚表项」,形成⼆级分⻚。如下图所示:

 那么为什么不分级的⻚表就做不到这样节约内存呢?我们从⻚表的性质来看,保存在内存中的⻚表承担的 职责是将虚拟地址翻译成物理地址。假如虚拟地址在⻚表中找不到对应的⻚表项,计算机系统就不能⼯作 了。所以⻚表⼀定要覆盖全部虚拟地址空间,不分级的⻚表就需要有 100 多万个⻚表项来映射,⽽⼆级分 ⻚则只需要 1024 个⻚表项(此时⼀级⻚表覆盖到了全部虚拟地址空间,⼆级⻚表在需要时创建)。

这一切都是借助局部性原理

再进一步的,到了四级页表,发现页表占用的内存空间更少了

 TLB

多级⻚表虽然解决了空间上的问题,但是虚拟地址到物理地址的转换就多了⼏道转换的⼯序,这显然就降 低了这俩地址转换的速度,也就是带来了时间上的开销。 程序是有局部性的,即在⼀段时间内,整个程序的执⾏仅限于程序中的某⼀部分。相应地,执⾏所访问的 存储空间也局限于某个内存区域,我们就可以利⽤这⼀特性,把最常访问的⼏个⻚表项存储到访问速度更快的硬件,于是计算机科学家们, 就在 CPU 芯⽚中,加⼊了⼀个专⻔存放程序最常访问的⻚表项的 Cache,这个 Cache 就是 TLB (Translation Lookaside Buffer) ,通常称为⻚表缓存、转址旁路缓存、快表等

 在 CPU 芯⽚⾥⾯,封装了内存管理单元(Memory Management Unit)芯⽚,它⽤来完成地址转换和 TLB 的访问与交互。 有了 TLB 后,那么 CPU 在寻址时,会先查 TLB,如果没找到,才会继续查常规的⻚表。 TLB 的命中率其实是很⾼的,因为程序最常访问的⻚就那么⼏个

段页式内存管理:

  • 先将程序划分为多个有逻辑意义的段,也就是前⾯提到的分段机制;
  • 接着再把每个段划分为多个⻚,也就是对分段划分出来的连续空间,再划分固定⼤⼩的⻚;

 段⻚式地址变换中要得到物理地址须经过三次内存访问:

  • 第⼀次访问段表,得到⻚表起始地址;
  • 第⼆次访问⻚表,得到物理⻚号;
  • 第三次将物理⻚号与⻚内位移组合,得到物理地址。

可⽤软、硬件相结合的⽅法实现段⻚式地址变换,这样虽然增加了硬件成本和系统开销,但提⾼了内存的 利⽤率。

Linux内存管理

  •  程序所使⽤的地址,通常是没被段式内存管理映射的地址,称为逻辑地址;
  • 通过段式内存管理映射的地址,称为线性地址,也叫虚拟地址;

Linux 内存主要采⽤的是⻚式内存管理,但同时也不可避免地涉及了段机制。

 Linux 系统中的每个段都是从 0 地址开始的整个 4GB 虚拟空间(32 位环境下),也就是所有的段的起始 地址都是⼀样的。这意味着,Linux 系统中的代码,包括操作系统本身的代码和应⽤程序代码,所⾯对的 地址空间都是线性地址空间(虚拟地址),这种做法相当于屏蔽了处理器中的逻辑地址概念,段只被⽤于 访问控制和内存保护。

Linux虚拟地址空间的分布:

内核空间与⽤户空间的区别: 进程在⽤户态时,只能访问⽤户空间内存; 只有进⼊内核态后,才可以访问内核空间的内存

每个虚拟内存中的内核地址,其实关联的都是相同的物理内 存。

⽤户空间分布的情况:

 从低到⾼分别是 7 种不同的内存段:

  1. 程序⽂件段,包括⼆进制可执⾏代码;
  2. 已初始化数据段,包括静态常量;
  3. 未初始化数据段,包括未初始化的静态变量;
  4. 堆段,包括动态分配的内存,从低地址开始向上增⻓;
  5. ⽂件映射段,包括动态库、共享内存等,从低地址开始向上增⻓(跟硬件和内核版本有关);
  6. 栈段,包括局部变量和函数调⽤的上下⽂等。栈的⼤⼩是固定的,⼀般是 8 MB 。当然系统也提供 了参数,以便我们⾃定义⼤⼩

 堆和⽂件映射段的内存是动态分配的。⽐如说,使⽤ C 标准库的 malloc() 或者 mmap() ,就可以分别在堆和⽂件映射段动态分配内存

 总结:

为了在多进程环境下,使得进程之间的内存地址不受影响,相互隔离,于是操作系统就为每个进程独⽴分 配⼀套虚拟地址空间,每个程序只关⼼⾃⼰的虚拟地址就可以,实际上⼤家的虚拟地址都是⼀样的,但分 布到物理地址内存是不⼀样的。作为程序,也不⽤关⼼物理地址的事情。 每个进程都有⾃⼰的虚拟空间,⽽物理内存只有⼀个,所以当启⽤了⼤量的进程,物理内存必然会很紧 张,于是操作系统会通过内存交换技术,把不常使⽤的内存暂时存放到硬盘(换出),在需要的时候再装 载回物理内存(换⼊)。 那既然有了虚拟地址空间,那必然要把虚拟地址「映射」到物理地址,这个事情通常由操作系统来维护。 那么对于虚拟地址与物理地址的映射关系,可以有分段和分⻚的⽅式,同时两者结合都是可以的。 内存分段是根据程序的逻辑⻆度,分成了栈段、堆段、数据段、代码段等,这样可以分离出不同属性的 段,同时是⼀块连续的空间。但是每个段的⼤⼩都不是统⼀的,这就会导致内存碎⽚和内存交换效率低的 问题。 于是,就出现了内存分⻚,把虚拟空间和物理空间分成⼤⼩固定的⻚,如在 Linux 系统中,每⼀⻚的⼤⼩ 为 4KB 。由于分了⻚后,就不会产⽣细⼩的内存碎⽚。同时在内存交换的时候,写⼊硬盘也就⼀个⻚或 ⼏个⻚,这就⼤⼤提⾼了内存交换的效率。 再来,为了解决简单分⻚产⽣的⻚表过⼤的问题,就有了多级⻚表,它解决了空间上的问题,但这就会导 致 CPU 在寻址的过程中,需要有很多层表参与,加⼤了时间上的开销。于是根据程序的局部性原理,在 CPU 芯⽚中加⼊了 TLB,负责缓存最近常被访问的⻚表项,⼤⼤提⾼了地址的转换速度。 Linux 系统主要采⽤了分⻚管理,但是由于 Intel 处理器的发展史,Linux 系统⽆法避免分段管理。于是 Linux 就把所有段的基地址设为 0 ,也就意味着所有程序的地址空间都是线性地址空间(虚拟地址),相 当于屏蔽了 CPU 逻辑地址的概念,所以段只被⽤于访问控制和内存保护。 另外,Linxu 系统中虚拟空间分布可分为⽤户态和内核态两部分,其中⽤户态的分布:代码段、全局变量、 BSS、函数栈、堆内存、映射区。

[1] https://www.cnblogs.com/thrillerz/p/7992180.html

[2] https://m.sohu.com/a/285246608_700886

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值