• 实模式到保护模式的过渡


    本文主要参考《深入分析linux内核》,配图都来自这本书,加入了一些自己的理解。

    页目录项的位定义

    页表项定义

    本文只会对012位解释,0present表示是否在内存中

      1 /*
      2 * .org new-lc,fill,这条汇编程序指令将本节的位置计数器提前至new-lc(New Location Counter)处,中间的字节被填充fill值,默认为0
      3 * .fill repeat,size,value 如果size大于8也只会取8,以size为一组,重复repeat次,填充value值
      4 * 只能在本节中往前,计数器只能增加,所以第一条将位置计数器变为0xc000000+0x100000+0x1000,因为编译后这些地址都是虚拟地址,所以
      5 * 为内核地址需要加上__PAGE_OFFSET,而内核前1M有特殊用途,所以从1M之后开始初始化内存
      6 * swapper_pg_dir为全局页目录起始地址,第0,1项是映射的pg0和pg1,共8M,第3-767项为空,第768,769项也为pg0和pg1,第770-1023项为空
      7 * 所以0x1000手动完成了全局页目录的初始化,用户区和内核区的开始两项的映射相同
      8 */
      9 .org 0x1000
     10 ENTRY(swapper_pg_dir)
     11 .long 0x00102007
     12 .long 0x00103007
     13 .fill BOOT_USER_PGD_PTRS - 2, 4, 0
     14 /* default: 766 entries */
     15 .long 0x00102007
     16 .long 0x00103007
     17 /* default: 254 entries */
     18 .fill BOOT_KERNEL_PGD_PTRS - 2, 4, 0
     19 
     20 /*
     21 * The page tables are initialized to only 8MB here - the final page
     22 * tables are set up later depending on memory size.
     23 * 后文会介绍完成对这两个页表的初始化
     24 */
     25 .org 0x2000
     26 ENTRY(pg0)
     27 
     28 .org 0x3000
     29 ENTRY(pg1)
     30 
     31 /*
     32 * empty_zero_page must immediately follow the page tables ! (The
     33 * initialization loop counts until empty_zero_page)
     34 * 0页表是不允许访问的,全是0
     35 */
     36 
     37 .org 0x4000
     38 ENTRY(empty_zero_page)
     39 
     40 .org 0x5000
     41 
     42 /*
     43 * Real beginning of normal "text" segment
     44 * 这里开始存放内核的代码段
     45 */
     46 ENTRY(stext)
     47 ENTRY(_stext)
     48 
     49 /*
     50 * This starts the data section. Note that the above is all
     51 * in the text section because it has alignment requirements
     52 * that we cannot fulfill any other way.
     53 * 内核data段
     54 */
     55 .data
     56 
     57 ALIGN
     58 
     59 
     60 /*
     61 * Initialize page tables
     62 * 开始初始化pg0和pg1两组页表,pg0- __PAGE_OFFSET就是pg0的起始物理地址0x102000
     63 */
     64 movl $pg0 - __PAGE_OFFSET, %edi /* initialize page tables */
     65 movl $007, %eax        
     66 /* "007" doesn't mean with right to kill, but PRESENT+RW+USER
     67 */
     68 2:
     69 /* 将eax中的值存入edi指定的位置中,因而第一次回在pg0第0项处存入0x007,对应的页就是起始物理地址为0的4KB物理内存,edi自动增加4,一项为4个byte*/
     70 stosl
     71 /* eax+4K,代表下一页*/
     72 add $0x1000, %eax
     73 /* 如果edi还未到0x4000,继续循环,即初始化了pg0和pg1两张页表 */
     74 cmp $empty_zero_page - __PAGE_OFFSET, %edi
     75 jne 2b
     76 
     77 /*
     78 * Enable paging
     79 */
     80 3:
     81 movl $swapper_pg_dir - __PAGE_OFFSET, %eax
     82 movl %eax, %cr3        /* set the page table pointer.. */
     83 movl %cr0, %eax
     84 orl $0x80000000, %eax
     85 /* 此处启用cr0的最高位PG允许位,表示开启分页机制,但因为eip中存储的仍为内存的物理地址,所以cpu对eip中的地址通过分页机制解析出来将是用户区
     86 * 的,如果不映射到内存开始8M位置,那儿eip解析出来的物理地址就是无效的,后面的指令就无法执行了,而我们需要的是按当前顺序继续执行下去,完成从
     87 * 实模式到保护模式的平稳过渡,内核初始化完成之后会删除用户区的这两个映射
     88 *  ..and set paging (PG) bit
     89 */
     90 movl %eax, %cr0
     91 /* flush the prefetch-queue,这是intel所建议的操作,逻辑上无作用,但可以将流水线上其他指令丢弃,避免乱序执行的影响 */
     92 jmp 1f            
     93 1:
     94 /* 上面的jmp是短跳转,EIP中仍然是物理地址,所以需要手动去完成EIP刷新,下一个1:在编译生成的时候采用的是虚拟地址,所以跳转到这个虚拟地址就
     95 * 真正的完成了EIP的虚拟地址更新,也就完成了实模式到保护模式的过渡
     96 */
     97 movl $1f, %eax
     98 jmp *%eax        /* make sure eip is relocated */
     99 1 :
    100 /* Set up the stack pointer */
    101 lss stack_start, %esp

    此时内存映射关系

    之后linux会根据开机加电后的BIOS探测到的物理内存信息进行内存的初始化,生成一张内存构成图,下一节将会是这些内容。

  • 相关阅读:
    hihoCoder #1077 RMQ问题再临-线段树
    ms sql 获取字符串首字母
    如何设置gen_server在退出时执行相关操作
    C++拾遗
    [置顶] Linux下文件和目录权限说明
    Android百度地图之显示地图
    USACO March. 2012
    JNI之HelloWorld
    复习C语言系列二:动态调用函数指针数组
    HDU2527:Safe Or Unsafe(哈弗曼树)
  • 原文地址:https://www.cnblogs.com/hmxb/p/4923225.html
Copyright © 2020-2023  润新知