Linux中国 Linux中国门户站!
设为主页 设为主页
收藏本站 收藏本站
 
当前位置 :首页 ->Linux技术 ->系统管理 ->正文

基于i386体系结构的Linux实现特点剖析——内存与进程

来源:Linux-cn.com 作者:Webmaster 时间:2007-05-05 点击: [收藏] [投稿]

四、内存管理

1、基本框架

Linux内核的设计要考虑到在各种不同的微处理器上的实现,还有考虑到在64位的微处理器(如Alpha)上的实现,所以不能仅仅针对i386结构来设计它的映射机制,而要以只要假象的、虚拟的微处理器和MMU(内存管理单元)为基础,设计出一种通用的模式,再把它分别落实到具体的微处理器上。因此,Linux内核的映射机制被设计成三层,在页面目录和页表之间增设了一层“中间目录”。在代码中,页面目录称为PGD,中间目录称为PMD,而页表称为PT。PT的表项称为PTE。PGD,PMD,PT均为数组,相应的,在逻辑上也把线性地址从高到低分为4各位段,个占若干位,分别用作目录PGD的下标、中间目录PMD的下标、页表中的下标和物理页面内的位移。

就i386微处理器来说,CPU实际上不是按三层而是按两层的模型来进行地址映射,这就需要将虚拟的三层映射落实到具体的两层的映射,跳过中间的PMD层次。

2、地址映射的全过程

i386微处理器一律对程序中的地址先进行段式映射,然后才能进行页式映射。而Linux所采用的方法实际上使段式映射的过程中不起什么作用。

下面通过一个简单的程序来看看Linux下的地址映射的全过程:



  #include 
  greeting()
  {
      printf(“Hello world!
”);
   }
  main()
  {
      greeing();
  }

该程序在主函数中调用greeting 来显示“Hello world!”,经过编译和反汇编,我们得到了它的反汇编的结果。


08048568:
8048568: 55                push1 %ebp
8048856b:89 e5             mov1 %esp,%ebp
804856b: 68 04 94 04 08    push1 $0x8048404
8048570: e8 ff fe ff ff    call 8048474 <_init+0x84>
8048575: 83 c4 04          add1 $0x4,%esp
8048578: c9                leave
8048579: c3                ret
804857a: 89 f6             mov1 %esi,%esi
0804857c 
: 804857c: 55 push1 %ebp 804857d: 89 e5 mov1 %esp,%ebp 804857f: e8 e4 ff ff ff call 8048568 8048584: c9 leave 8048585: c3 ret 8048586: 90 nop 8048587: 90 nop

从上面可以看出,greeting()的地址为0x8048568。在elf格式的可执行代码中,总是在0x8000000开始安排程序的“代码段”,对每个程序都是这样。

当程序在main中执行到了“call 8048568”这条指令,要转移到虚拟地址8048568去。

首先是段式映射阶段。地址8048568是一个程序的入口,更重要的是在执行的过程中有CPU的EIP所指向的,所以在代码段中。I386cpu使用CS的当前值作为段式映射的选择子。

内核在建立一个进程时都要将其段寄存器设置好,把DS、ES、SS都设置成_USER_DS,而把CS设置成_USER_CS,这也就是说,在Linux内核中堆栈段和代码段是不分的。


                         Index           TI DPL
#define_KERNEL_CS 0x10  0000 0000 0001 0|0|00  
#define_KERNEL_DS 0x18  0000 0000 0001 1|0|00  
#define_USER_CS 0x23  0000 0000 0010 0|0|11
#define_USER_DS 0x2B  0000 0000 0010 1|0|11
_KERNEL_CS:         index=2,TI=0,DPL=0
_KERNEL_DS:         index=3,TI=0,DPL=0    
_USERL_CS:          index=4,TI=0,DPL=3
_USERL_DS:          index=5,TI=0,DPL=3

TI全都是0,都使用全局描述表。内核的DPL都为0,最高级别;用户的DPL都是3,最低级别。_USER_CS在GDT表中是第4项,初始化GDT内容的代码如下:


  ENTRY(gdt-table)
      .quad 0x0000000000000000   /* NULL descriptor */
      .quad 0x0000000000000000   /* not used */
      .quad 0x00cf9a00000ffff    /* 0x10 kernel 4GB code at 0x00000000 */
      .quad 0x00cf9200000ffff    /* 0x18 kernel 4GB data at 0x00000000 */
      .quad 0x00cffa00000ffff    /* 0x23 user 4GB code at 0x00000000 */
      .quad 0x00cff200000ffff    /* 0x2b user 4GB data at 0x00000000 */

  GDT 表中第一、二项不用,第三至第五项共四项对应于前面的四个段寄存器的数值。
将这四个段描述项的内容展开:

  K_CS:  0000 0000 1100 1111 1001 1010 0000 0000
         0000 0000 0000 0000 1111 1111 1111 1111
  K_DS:  0000 0000 1100 1111 1001 0010 0000 0000
         0000 0000 0000 0000 1111 1111 1111 1111
  U_CS:  0000 0000 1100 1111 11111 1010 0000 0000
         0000 0000 0000 0000 1111 1111 1111 1111
  U_DS:  0000 0000 1100 1111 1111 0010 0000 0000
         0000 0000 0000 0000 1111 1111 1111 1111

这四个段描述项的下列内容都是相同的。

       
    ·BO-B15/B16-B31 都是0     基地址全为0
    ·LO-L15、L16-L19都是1     段的界限全是0xfffff
    ·G位都是1                 段长均为4KB
    ·D位都是1                 32位指令
    ·P位都是1                 四个段都在内存中
    不同之处在于权限级别不同,内核的为0级,用户的为3级。

由此可知,每个段都是从地址0开始的整个4GB地虚存空间,虚地址到线性地址的映射保持原值不变。

再回到greeting 的程序中来,通过段式映射把地址8048568映射到自身,得到了线性地址。

 如果您对本文有任何疑问或者建议,请到讨论区发表您的意见: >> 论坛入口 <<



上一篇:基于i386的Linux实现特点剖析——关于中断   下一篇:给Red Hat8.0加上五笔输入法

文章评论】 【收藏本文】 【推荐好友】 【打印本文】 【我要投稿】 【论坛讨论
更多相关文章
Power by linux-cn.com 粤ICP备05006655号