linux有意避开了分段机制

来源:互联网 发布:c语言文件指针 编辑:程序博客网 时间:2024/04/28 01:55
在逻辑地址转为线性地址的时候,我们说过,用段寄存器中的值的前13位当作索引来索引全局描述符表,linux中,滑稽的是,它有意地采用数值来绕开了分段机制,对内存的管理主要采用了分页机制。是怎样的呢?先看看,linuxGTD中放入了些什么。查看文件 arch/i386/head.S

.quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */

  .quad0x00cf92000000fff  /* 0x68 kernel 4GB data at 0x00000000 */

  .quad0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */

  .quad0x00cff2000000ffff/* 0x7b user 4GB data at 0x00000000 */

这里仅仅摘下了12-15项的内容,且看下面。我们再来看看在在各个段寄存器中的选择子是写什么数字,看看include/asm-i386/segment.h中的定义。

include/asm-i386/segment.h

  #defineGDT_ENTRY_DEFAULT_USER_CS 14

  #define__USER_CS (GDT_ENTRY_DEFAULT_USER_CS * 8 + 3)

  #defineGDT_ENTRY_DEFAULT_USER_DS 15

  #define__USER_DS (GDT_ENTRY_DEFAULT_USER_DS * 8 + 3)

  #defineGDT_ENTRY_KERNEL_BASE 12

  #defineGDT_ENTRY_KERNEL_CS (GDT_ENTRY_KERNEL_BASE + 0)

  #define__KERNEL_CS (GDT_ENTRY_KERNEL_CS * 8)

  #defineGDT_ENTRY_KERNEL_DS  (GDT_ENTRY_KERNEL_BASE + 1)

  #define__KERNEL_DS (GDT_ENTRY_KERNEL_DS * 8)





把其中的宏替换成数值,则为:

  #define__USER_CS 115        [000000001110  0  11]

  #define__USER_DS 123        [000000001111  0  11]

  #define__KERNEL_CS 96      [00000000 1100  0 00]

  #define__KERNEL_DS 104    [00000000 1101  0  00]

  方括号后是这四个段选择符的16位二制表示,它们的索引号和T1字段值也可以算出来了

  __USER_CS             index= 14   T1=0

  __USER_DS              index= 15   T1=0

  __KERNEL_CS          index=  12  T1=0

  __KERNEL_DS          index= 13   T1=0





可以看到,用户代码段的寄存器中USRE_CS 115

把它拆开,用来索引GDT时,即找到第14项,可以看到

.quad 0x00cffa000000ffff /* 0x73 user 4GB code at 0x00000000 */



16-31位全部为0,即段的起始地址为0,那么逻辑地址转为线性地址

0x00000000+偏移=偏移。

这不是偶然现象,而是所有的情况都是这样的,不信可以在linux下用gdb调试程序输出寄存器的值看看,是不是115或者123。(段寄存器)



这里可以看自己随便写的函数然后调试的结果。












可以看到cs等几个寄存器的值一直都是用户CSDS115 123



不是偶然》。。。。







那么可以看到,逻辑地址和线性地址有着同样的形式(即数字上是一样的。)





linux内存管理主要是采用分页,避开了分段(但硬件要求分段,故采用了避开的手段),但后面的3位(段选择子后面3位用来表示段的级别的,00-11是有用的,访问GDT还是LDT是有用的)。





这个图可能更清楚:




继而由线—物



我们知道Linux中用户进程线性地址能寻址的范围是03G,那么是不是需要提前先把这3G虚拟内存的页表都建立好呢?一般情况下,物理内存是远远小于3G的,加上同时有很多进程都在运行,根本无法给每个进程提前建立3G的线性地址页表。Linux利用CPU的一个机制解决了这个问题。进程创建后我们可以给页目录表的表项值都填0CPU在查找页表时,如果表项的内容为0,则会引发一个缺页异常,进程暂停执行,Linux内核这时候可以通过一系列复杂的算法给分配一个物理页,并把物理页的地址填入表项中,进程再恢复执行。当然进程在这个过程中是被蒙蔽的,它自己的感觉还是正常访问到了物理内存。





0 0