《程序员的自我修养》中介绍详细 带着相关问题分析:
1>为什么要引入虚拟内存 2>内存分布及各部分详解 3>分页分段机制 4>如何进行地址映射
Q:为什么要有虚拟内存?? A:有了虚拟内存,每个进程都独享一个较大的空间,这样能够用占用内存更大的程序。
.bss段的意义探索:
使用objdump查看各个段的信息:
size为该段大小,VMA虚拟内存空间,LMA加载内存地址,File off在文件中的偏移(编译过程中不分配内存) 注意: 1>.text偏移不是0==>ELF Header(大小是52字节) 2>.bss段没有和下一个段偏移相同==>文件中么有》》》??放哪里了
test.o文件: ----------- ELF Header-----0x00000000 ==>段表位置,段表描述符数量,大小,ELF文件大小等 .text .data .bss ... .Section table ==>段表???各个段信息 .systab ==>符号表 //变量 方法 .rel.test ==>重定位表 ------------段表:
偏移 0XF4==>段表位置
.bss段里数据都是0,所以没有必要在文件中分配位置节省.o文件的大小,但是必须得知道有这么个东东,所以段表就完成了这工作。
提到符号表和重定位表: .o文件的链接: 合并所有obj文件的段,调整偏移和段长,合并符号表并进行符号解析,分配虚拟内存(链接的核心就是符号重定位)
内存管理==>软硬结合:硬件(MMU内存管理单元)+软件(页表等信息)
实模式:物理内存 = 段地址DS左移4位 + IP(逻辑地址) --- why?: 搜噶! 实模式依然是段式管理,存在着段寄存器和IP寄存器。32位系统的地址总线是20条,但是段寄存器是从X86体系下继承过来的,那么段寄存器的大小是16位,规定每个内存段的起始地址必须是16的倍数,那么一个段的物理内存为2^16=64K。但是20和16明显对比不上。怎么办呢?让我们来看看16的二进制写法,0010 000。也就是低4位全部为0。那么20位的低4为全部为0,剩下的高16位就能够放进去了。所以寻址方式成了这个样子:如果要寻找某个数据的内存,那么要从DS寄存器里找,DS寄存器里放的是高16位,那么得左移4位得到所在段的地址,再加上IP寄存器里的偏移量,最后获得数据的地址,也就是物理地址。 开机时强制进入实模式,1M大小内存 ==> 加载完OS后就进入保护模式。 --- 保护模式:增加了两个寄存器 = GDT(全局的段描述符表的地址)+LDT(局部) 保存了内存的起始地址 内存段大小 访问权限 --- 段寄存器:(16位) 但是(IP是32位) 15 1 0 |。。。。。。。。。。|TI| RPL| 低两位:RPL表示权限级别==00内核态 11表示用户态 TI :0使用GDTR 1使用LDTR 其他13位表示8192个描述符表(系统已经用了12个) CPU其他重要寄存器: CR0:最高位叫PG位,0:未开启分页机制,1:开启分页机制 CR2:发生缺页异常的虚拟地址 CR3:页目录的起始地址 CR4:PAE位 物理地址扩展 0:未开启 1:开启 地址映射: -- GDT[DS>>3].baseadd(基址) + IP(逻辑地址) == 线性地址 如果没有开启页表机制==> 线性地址==物理地址 如果开启页表机制==> 线性地址 == 虚拟地址 32位的虚拟地址分3 份 : 10 + 10 + 12 页目录下标1024 页表下标1024 页面上的偏移(4K) 1024*1024*4K==>>4G 得到物理地址