Chinaunix首页 | 论坛 | 博客
  • 博客访问: 913452
  • 博文数量: 119
  • 博客积分: 2493
  • 博客等级: 大尉
  • 技术积分: 2363
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-03 14:00
文章分类

全部博文(119)

文章存档

2013年(19)

2012年(100)

分类: LINUX

2012-07-21 15:45:50

一,linux绕过了段机制而主要采用了页机制实现内存管理。原因如下:
            1,为了使得内存管理变得简单,绕过了段机制,虚拟地址=线性地址。
            2,为了方便移植到多个硬件平台,因为很多RISC处理器不支持段机制。
----------------------------------------------------------------------------------------------
二,进程的地址空间。

    linux下每个进程都有4GB的地址空间=1GB的内核空间  + 3GB的用户空间
    1,进程切换时,虚拟地址空间也随之切换。唯有3GB的用户空间切换,1GB的内核空间
          是所用进程共享的,常驻内存。
    2,虚拟地址空间切换,页表也随之切换。
----------------------------------------------------------------------------------------------
三,虚拟地址映射到物理地址。

      地址映射应该分两部分分析,内核空间和用户空间。
      内核空间使用的是线性映射,很好理解。
      用户空间使用的是页表来映射。
 ---------------------------------------------------------------------------------------------   
          3.1,内核空间的地址映射到物理地址空间。
    
  内核空间地址映射函数如下:

  1. #define __va(x) ((void *)((unsigned long)(x) PAGE_OFFSET))  // 虚拟地址 = 物理地址x +  3GB
  2. #define __pa(x) ((unsigned long) (x) - PAGE_OFFSET)         //物理地址  = 虚拟地址x  -  3GB 
----------------------------------------------------------------------------------------------
   
               3.2,用户空间映射到物理地址空间。
        linux 采用了三级页表,实际上linux可以通过启用或禁用中间目录来启用两级和
       三级分页(使用相同的代码),在32位的x86体系上就用的两级页表。
 
----------------------------------------------------------------------------------------------
          先以两级页表说说虚拟地址是如何映射成为物理地址的吧。

         
               进程切换,首先是从新的进程的PCB中读出pgd,然后写入CR3中,cr3 = __pa(pgd)
 CR3相当于页表的根所在,CR3中存放的是页目录的基址,该基址 偏移量(线性地址的前10位)
  = 页表的基址,页表的基址 偏移量(线性地址的中间10位)=物理页的基址,物理页的基址
 偏移量 = 物理地址。

下面小结物理地址的计算:

  1. cr3 Page Directory (10 MSB) = 指向 table_base
  2. table_base Page Table (10 中间位) = 指向 page_base
  3. page_base Offset = 物理地址 (获得页框)        
         
----------------------------------------------------------------------------------------------
四,使用内核模块程序读出当前进程的CR3和pgd进行对比。


  1. #include <asm/system.h>
  2. #include <linux/mm.h>
  3. #include <linux/mm_types.h>
  4. #include <linux/sched.h>
  5. #include <linux/types.h>

  6. static unsigned long read_pgd(void)
  7. {
  8.     return (unsigned long)current->mm->pgd;
  9. }

  10. static int __init printvm_init(void)
  11. {
  12.     unsigned long cr3;
  13.     unsigned long pgd = 0;

  14.     cr3 = read_cr3();
  15.     printk("cr3----------------->0x%lx\n",cr3);
  16.     pgd = __pa(read_pgd());
  17.     printk("pgd----------------->0x%lx\n",pgd);
  18.     return 0;
  19. }

  20. static void __exit printvm_exit(void)
  21. {
  22.     printk("--------------------->exit!\n");
  23.     return ;
  24. }

  25. module_init(printvm_init);
  26. module_exit(printvm_exit);
内核里面写CR3:


结论:
      pgd里面存放的是虚拟地址,通过__pa()后映射为物理地址,然后将该值写入到CR3.

----------------------------------------------------------------------------------------------




阅读(1961) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~