linux内存的寻址方式
1. 为什么需要内存管理
- 效率问题
- 可能你会想到当c程序跑的时候把b程序数据写到磁盘上,等运行b的时候再数据从磁盘写回来,先不说无法满足b,c程序并行跑的需求,就连频繁的io操作带来的耗时问题也是无法接收的。
- 进程地址隔离问题
- 除了效率问题,reserved给进程的空间如果需要被别的进程访问会出现崩溃。比如a进程访问的空间是前10M,但是a程序中有一段代码访问10-110M的话就有可能导致b程序的崩溃,所以进程的地址空间需要彼此隔离。
- 重定位问题
- 现实场景中不可能是单任务在分好的内存中运行,当多任务并行跑的情况下在动态申请释放内存的时候有可能申请到其它进程里的地址,这时候需要重定位到新的地址。
2. 内存地址
-
逻辑地址
逻辑地址 = 段地址 + 偏移;包含在机器语言指令中,用来指定一个操作数或者一条指令的地址
-
线性地址
线性地址由逻辑地址经过分段单元得到,是一个32位无符号整数,用16进制表示
-
物理地址
用于内存芯片级内存单元寻址,他们与微处理器的地址引脚到内存总线上的电信号对应
3.硬件中的分段
a. 分段的基本功能
-
解决进程间隔离以及重定位问题
-
将程序分段,把整个段平移到任何位置后,段内的地址相对段基址不变,无论段基址是多少,只要给出段内偏移地址,cpu就能访问到正确的指令。
-
于是加载用户程序时,只要将整个段的内容复制到新的位置,再将段基址寄存器中的地址改成该地址,程序便可准确地运行,因为程序中用的是段内偏移地址,相对新的段基址,该偏移地址处的内容内容还是一样的。
-
这个动作是在硬件里做的,但是有的硬件是没有分段机制的,作为跨平台的linux就用了具有更通用性的分页机制来解决线性地址到虚拟地址到物理地址的转换。
b. 逻辑地址==>线性地址
-
段选择符(16位)
- index:指定放在GDT or LDT中相应段描述符的入口
- TL: table indicator,指明段描述符是在GDT 还是 在 LDT中
- RPL: 请求者特权级
-
段描述符(8字节)
- base: 包含段的首字节的线性地址
- limit:存放段中最后一个内存单元的偏移量,从而决定段的长度
- DPL: 描述符特权级,用于限制对这个段的存取
-
快速访问段描述符
- 段寄存器仅仅存放段选择符,每当一个段选择符被装入段寄存器,相应的段描述符就由内存装入到对应的非编程CPU寄存器中,当段寄存器不变时,只需要访问cpu寄存器即可
-
逻辑地址的转换
-
通过gdtr,定位到gdt
-
通过段选择符中的index定位到具体的段描述符
-
通过段描述符中的base + 逻辑地址的offset 计算得到线性地址
-
4.linux中的分段
-
Linux以非常有限的方式使用分段,Linux更喜欢分页机制,因为:
- 当所有进程使用相同的段寄存器值,内存管理变的更简单,他们能够共享同一组线性地址
- Linux的设计目标是多平台通用,而RISC体系结构对分段的支持有限
-
Linux中主要的段描述符字段值
-
Linux 全局描述符表 段选择符
3.分页机制
a. 基本功能
-
为提高效率,线性地址被分为固定长度为单位的组,称为页。页内部连续的线性地址被映射到连续的物理地址中
-
把线性地址映射到物理地址的数据结构称之为页表
-
正在使用的页目录的物理地址存放在控制器cr3中,线性地址内的Directory字段决定页目录中的目录项,目录项指向适当的页表。地址的table字段依次又决定页表中的表项,表项含有页所在页框的物理地址
-
8086处理器的分页机制
b. 数据结构
- 页表和页目录的数据结构
- present: 1表示所指的页就在主存中,0表示这一页不在主存
- accessed: 当分页单元对相应的页框进行寻址时就设置这个标识
- dirty:每当对一个页框进行写操作时就设置这个标识
- read/write:含有页或页表的存取权限
- User/Supervisor:含有访问页或页表所需要的特权级
- PCD/PWT :控制硬件告诉缓存处理页或页表的方式