操作系统为每个进程分配独立的内存–虚拟内存,每个进程都不能访问物理地址。**CPU中的内存管理单元(MMU)会将虚拟地址映射成物理地址。**如果程序要访问虚拟地址的时候,会映射成不同的物理地址,所以不同进程运行的时候,不会引起冲突。
因此我们可以清楚的分出:
- 实际存在硬件中的空间地址叫做物理内存地址。
- 程序使用的内存地址叫做虚拟内存地址。
操作系统通过分页和分段两种方式管理内存。
分段
操作系统将内存分为四段,分别是代码段、堆、栈和data段。分段地址下的虚拟地址分为两部分:段选择因子和段内偏移量。
段选择因子
段选择子分为两部分:段号和段特权标志位。**段选择子中最重要的是段号,用做段表的索引。**段表中存储的是段起始地址、段界限和特权等级等。
段的偏移量在0到段的界限之间,如果段偏移量合法的话,段起始地址+段偏移量=物理地址。
分段的缺点
内存分段会引起两个问题,
- 第一个是内存碎片的问题。
- 第二个是内存交换效率低的问题。
分页
分段的好处是能产生连续的内存空间,但是会出现外部内存碎片和内存交换效率低的问题。
要解决这些问题,就引出了分页管理。分页下虚拟地址分为两部分:页号和页内偏移量。
页号
页号是页表的索引,页表包含物理页每页物理内存的基地址,基地址与内内偏移量就形成了物理内存地址。
分页管理是把整个虚拟和物理内存空间切成一段段固定尺寸的大小,这样连续且尺寸固定的内存空间,叫做页。
当今曾访问的虚拟地址在页表中查不到时,系统会产生缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后返回用户空间,恢复进程的运行。
分页的缺点
分页的缺点就是会产生内部内存碎片,因为页是最小可分配单位,所以不足一页的程序也会分配一页的内存。
地址的映射方式
分页方式下的地址映射分为三步:
- 把虚拟内存地址切分成页号和页内偏移量
- 根据页号,从页表里面,查询到对应的物理页号
- 根据物理页号,加上页内偏移量,就得到物理内存地址
参考下图**(图源小林coding)**
多页
我们设想一下,每个进程分配4GB的虚拟内存,按照分页管理的话,每页大小为4KB的话,那么估计是100多万页,一个页表项需要4个字节大小存储,存储页表就需要4MB(页表是放在内存里的)。那么多个进程的话,需要很大一部分内存来存储页表。多级页表可以解决上面的问题。
我们把100多万个页表项的单级页面再分页,一级页表分为1024个二级页表,每个二级页表包含1024个页表项。
Linux的内存布局
逻辑地址
程序所使用的地址,通常是没被段式内存管理映射的地址,称为逻辑地址
虚拟地址
通过段式内存管理映射的地址,称为线性地址,也叫虚拟地址。
转换步骤
(1)逻辑地址转线性地址
段起始地址+段偏移量=线性地址
(2)线性地址转物理地址
每个线性地址包含三部分,页目录,页表索引,页内偏移
页目录+页表索引=页表
页表+页内索引=物理地址