操作系统-内存管理
内存管理
虚拟内存
- 虚拟内存:操作系统为了将进程和物理内存隔离,设计的一种内存映射。
内存分段
虚拟地址
- 段选择子:
- 段号(段表的索引)
- 特权标志位
- 段内偏移量(0 ~ 段界限)
- 段表:
- 段的基地址
- 段的界限
- 权限等级
- 物理地址:段的基地址 + 段内偏移量
内存分段机制
- 将程序的虚拟地址分为4个段,每个段在段表中有一项,在这一项中找到段的基地址,再加上偏移量,就能找到物理内存中的地址:
- 代码段
- 数据段
- 堆段
- 栈段
内存分段的问题
- 内存碎片:
- 外部内存碎片:产生多个不连续的小物理内存,导致新的程序无法被装载。
- 内部内存碎片:程序的所有内存都被装载到物理内存中,但部分内存不常使用,导致内存浪费。
- 解决方式:
- 内存交换:通过交换技术,减少碎片。
- 内存交换效率低:
- 每次内存交换都需要将内存写到硬盘上,再从硬盘上读回到内存,硬盘访问速度较慢,导致性能瓶颈。
- 解决方式:
- 内存分页:通过分页管理提高效率。
内存分页
概念
- 将整个虚拟和物理内存空间切成固定大小的页(在Linux下,每一页的大小为4KB)。
- 虚拟内存和物理内存之间通过页表来映射。
内存管理单元 (MMU)
- MMU:负责将虚拟内存转换为物理内存。
- 当进程访问的虚拟地址在页表中查不到时,会产生一个缺页异常,进入内核空间分配物理内存、更新页表、最后返回用户空间,恢复进程的运行。
分页的优势
- 解决内存碎片:分页释放的内存是以页为单位,不会产生无法使用的小内存碎片。
- 提高内存交换效率:内存不足时,可以将不常使用的内存页面换出,换入时只需加载少量页,提高了效率。
- 延迟加载:程序运行时才加载需要的内存页面。
虚拟内存地址的结构
- 页号:页表的索引。
- 页表包含每页物理地址的基地址,加上偏移量即可得到物理地址。
- 页内偏移量:页内的具体位置。
内存地址转换过程
- 将虚拟内存地址切分为页号和偏移量。
- 根据页号,从页表查询对应的物理页号。
- 将物理页号加上偏移量,得到物理内存地址。
简单页表的问题
- 页表占用过多内存:在32位系统下,一个进程的页表需要100多万个页表项,占用4MB内存,多个进程会消耗大量内存。
解决方法:多级页表
- 多级页表:将页表再进行分页,形成页表的页表。
- 二级页表:一级页表覆盖整个虚拟地址空间,未被使用的一级页表项无需创建二级页表项,节省内存。
- 多级页表:64位系统中有4级页表:
- PGD(Page Global Directory):全局页
- PUD(Page Upper Directory):上层页
- PMD(Page Middle Directory):中间页
- PTE(Page Table Entry):页表项
多级页表转换效率问题
- TLB(Translation Lookaside Buffer):在CPU中存放最常访问的页表项的缓存,提升页表查找效率。
段页式内存管理
实现原理
- 结合内存分段和内存分页:
- 先将程序划分为多个有逻辑意义的段。
- 再将段划分为多个页。
访问过程
- 第一次访问段表,得到页表起始地址。
- 访问页表,得到物理页号。
- 将物理页号与页内偏移组合,得到物理地址。
优势
- 提高内存的利用率。
Linux 内存管理
虚拟地址空间分布
- 内核空间:
- 32位系统为1GB,64位系统为128TB,占据最高位。
- 用户空间:
- 32位系统为4GB,64位系统为128TB,占据最低位。
内核空间和用户空间的区别
- 进程在用户态时只能访问用户空间内存。
- 进入内核态后,才能访问内核空间内存。
- 每个虚拟内存中的内核地址实际上关联相同的物理内存,方便进程切换到内核态后访问内核空间。
用户空间分布
- 用户空间从低到高依次包含:
- 程序文件段:二进制可执行代码。
- 已初始化数据段:静态常量。
- 未初始化数据段:未初始化的静态常量。
- 堆段:动态分配的内存,从低地址向上增长。
- 文件映射段:动态库、共享内存等,从低地址向上增长。
- 栈段:局部变量和函数调用的上下文,栈大小一般为8MB。
Linux 的内存管理策略
- Linux 主要采用分页管理,但由于 Intel 处理器的发展史,无法避免分段管理。
- Linux 将所有段的基地址设置为0,相当于所有程序的地址空间都是线性地址空间,段仅用于访问控制和内存保护。
- 用户态内存分布:代码段、全局变量、BSS、堆内存、映射区、函数栈。
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
Comment