Linux下虚拟地址映射
一、基本概念:
1、Linux下比较重要的点:虚拟地址空间、虚拟内存、交换分区
2、Linux下的四种地址:逻辑地址、线性地址、虚拟地址、物理地址
3、Linux下的两种映射:实模式下的地址映射、保护模式下的地址映射(内存的分段式映射、内存的分页式映射)
4、平时定义的变量,打印变量的地址是逻辑地址
5、cpu的位数是指cpu的计算能力,即一次性能计算的最长整数的字节(ALU的宽度)(数据总线的条数)
6、计算机的三线:控制总线、地址总线、数据总线
控制总线:用来发0和1,即读写信号
地址总线:在内存上定位地址
数据总线:在内存中获取数据或者写数据
二、分析过程:
1、在inter x86中,从第一个 8086(16位)开始,增加了4个寄存器(32位cpu也是16位寄存器,因为隶属于同一个体系),分别为 CS(代码段寄存器)、DS(数据段寄存器)、SS(堆栈段寄存器)(ES(扩展段寄存器))、IP(IP寄存器,存放偏移量)。(寄存器里存放的都是段开始地址)
2、实模式映射:
在这个8086CPU下,地址总线是二十条,物理内存在分段时,每段的起始位置必须是16的倍数(原因,16的倍数的二进制表示法它的低四位都是0,这样,16位的寄存器才可以放20位的地址),所以如果需要得到一个数据的地址的话,首先在它所在的段寄存器中拿到段起始地址时,再对它左移四位(恢复成20位地址),然后再加上它的IP偏移量(在段内的偏移量)。公式如下:
实地址模式(实模式:直接访问物理地址):DS<<4 + IP = 数据的地址(物理地址)
(偏移量/偏移地址/逻辑地址(段+偏移量))
未启动操作系统之前,会强制进入实模式状态,加载的内存为:2^20=1M
3、保护模式映射
(1)、80386(32位)为了添加内存的大小和访问权限,又就上面四个寄存器(CS、DS、SS(ES) 仍然保持16位,IP变为32位)外,添加了两个寄存器:
分别为GDTR(全局的段描述附表):存放GDT(全局的段描述附表)的地址,在内核
LDTR(局部的段描述附表):与CS、DS结构类似,存放的时LDT在GDT里面的序号、标志位以及权限
GDT是所有进程共享的,而LDT是每个进程私有的。
(2)、GDT是一个数组,数组的大小是 8192(由段可表示的数字决定的) - 12(系统预留) = 8180;
每一个项的大小是8byte =》 段描述符表项
GDT:
段描述符表项:
(3)、因为内存的详细信息都存于此,所以,CS、DS、SS(ES)不再存放内存的起始地址,而存放段信息再描述附表中的索引(下标),段寄存器的详细信息如下:
(4)、总结
保护模式下,地址映射图:
(5)、怎么判断内核有没有开启分页机制呢?
CPU其他重要寄存器
CR0:最高位叫PG位,0:未开启分页机制,1:开启分页机制
CR2:发生缺页异常的虚拟地址
CR3:页目录的起始地址
CR4:PAE位 物理地址扩展 0:未开启 1:开启
4、保护模式下 内存分页的地址映射
(1)、Linux下,内核给每一个程序的运行都会分配一个虚拟地址空间,给CPU的位数有关,如果是32位,则2^32=4G 大小,还有,如果段描述表项中G标志位为1,则表示该段描述表项的单位为页面,所以内存的大小就等于2^20(1M)*4K = 4G.
(2)、32位系统:二级页面映射;36位:三级页面映射 ;64位:四级页面映射
(3)、操作系统管理内存的方式是段页式(此说法不太准确,不全是,有的只有页式)
(4)、详细图解:
总结:
a. 程序操纵的地址都是虚拟空间上的地址,虚拟地址经过页表映射,得到物理地址
b.每个进程都有自己的页目录和页表,所以,虚拟地址连续,映射出来的物理地址不一定是连续的
c.每个进程的运行都需要分配物理页面,进程在运行时,内核会把当前进程运行的页面放在物理内存中,根据LRU最近最久未使用算法,会把时间长未被使用的页面放在交换分区,以提高系统的性能。
d.内核怎么管理物理内存?
在内核初始化时,先看物理内存的大小,除以4K,看能够划分N个页面。
开辟一个数组(大小为N,元素为物理页面,下标在PT里面存放着)
所以,物理页面的管理就是由一个动态开辟的数组来进行管理的
(5)可执行程序在磁盘上存储:
Load1 text 0x1000(页面对齐)
Load2 data 0x1000(页面对齐)
虚拟地址空间:
0x1000(页面对齐)
物理内存
0x1000(页面对齐)
所以,映射起来比较方便
地址映射总图如下:
三、小点整理
1、虚拟内存:a. 是内存管理的方式,由它负责把页面放在交换分区还是活动区;
b. 虚拟内存使操作系统给每个进程分配4G空间;
c. 保证了每个进程的虚拟地址空间是隔离的。
虚拟地址空间:防止程序直接访问物理空间,确保其安全性。
交换分区:磁盘空间,根据LRU最近最久未使用算法(相当于队列)选择一个页面交换出去,只有脏页(被修改过的页面)才会被交换(因为如果未被修改的话,可以直接被新开辟的页面覆盖)。
2、为什么所有的东西都可以用open打开?
因为Linux下有一个虚拟文件系统,屏蔽了底层协议,给上层提供了一种统一的接口API。
3、假设一个程序,要执行它(./),它不会马上开辟物理内存的,物理内存是到不能再拖的时候才开辟的,过程如下:
a.先发现页目录项是空的,分配页表项,并填充页目录项,重启地址映射
b.再发现页表项为空,则才分配物理内存,重启地址映射
因为程序的运行,虚拟内存分配时,基本是连续的,所以第一次访问时,容易发生中断,后续再进行访问时,就不经常发生了。这样做的好处是节省了物理内存。
4、问:能不能在用户空间定义一个指针指向内核空间的内存?
答案:不能,没权限
问:能不能在内核空间定义一个指针指向用户空间的内存?
答案:能,
问:但是用户空间的内存有可能有两种,要么在物理内存中,要么在交换区域里,如果在交换区域里,就找不到,这种现象怎么解决?
答:在访问时,遇到问题,交给内核,内核一旦检测到错误原因是访问的页面在交换分区里,就会启动MMU的页面置换,把这个页面置换出来以供访问。
5、上述所有映射都是针对于用户空间,而内核空间映射如下:
6、可以查看地址映射过程的工具:bochs(调试操作系统)