免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3714 | 回复: 4

[FreeBSD] FreeBSD页表映射的建立 [复制链接]

论坛徽章:
0
发表于 2007-10-14 17:43 |显示全部楼层
qiuhan
2007.8.17

今天我们继续用qemu来探索freeBSD第一个的页表映射的建立。
源码位于sys/i386/i386/locore.s

首先,我们简单说一下loader的作用。loader负责解析/boot/loader.rc,loader.conf等文件,把kernel、模块以及可能
的二进制文件加载到内存,并把加载的文件列表按照特定的格式写入内存(由modulep指向起始地址),把读取到的环境
变量的值按照name=value的格式写入内存(由envp指向起始地址), kernendp指向可用内存的位置(并不是kernel加载的结束
位置).这些信息都会保存在结构bootinfo内,最后loader把该结构的地址等信息作为参数传给内核的真正入口btext。
      |                |
      |----------------| 0xec6000 kernendp
      |----------------|
      |                |
      |----------------| 0xec5000 modulep
      |----------------|
      | envp           |
      |----------------| 0xec4000
      |----------------|
      | acpi ...       |
      |----------------|
      |----------------|
      | kernel         |   
      |----------------| 0x400000
      |      ...       |
      |----------------|
      |    bootinfo    |
      |----------------| 0x40a40
  loader 结束时内存映像结构图(其中的空白是由roundup造成的)

接下来,我们来看btext。对btext的调用可以理解为如下形式:
(*btext)(howto, bootdev, 0, 0, 0, &bootinfo)
调试中查看到的参数依次为:0x80000000 0xa0200000 0 0 0 0x00040a40
btext首先在0x472写入$0x1234告诉bios下次热启动,接着是常规的ebp压栈,esp赋给ebp,然后对eflags,fs,gs赋值,对bss段赋0值. 接着,依次调用
recover_bootinfo, identify_cpu, create_pagetables, 然后使能paging,最后跳转到begin,由begin执行init386, mi_startup.

recover_bootinfo负责把bootinfo的信息从0x40a40复制到内核的全局变量bootinfo中,并把howto和bootdev的值copy出来。执行后效果如下:
boothowto = 0x80000000
bootdev = 0xa0200000
(gdb) p/x *(struct bootinfo *)0xa4dc00 //这里使用的是实地址模块
$10 = {bi_version = 0x1, bi_kernelname = 0x5dbc4, bi_nfs_diskless = 0x0, bi_n_bios_used = 0x0, bi_bios_geom = {0x3fe0f3f,
    0x4f010f, 0x4f010f, 0x4f010f, 0x4f010f, 0x4f010f, 0x4f010f, 0x4f010f}, bi_size = 0x54, bi_memsizes_valid = 0x1,
  bi_bios_dev = 0x80, bi_pad = {0x0, 0x0}, bi_basemem = 0x27f, bi_extmem = 0x1fbc0, bi_symtab = 0xb2d8e8,
  bi_esymtab = 0xc3bbd4, bi_kernend = 0xec6000, bi_envp = 0xec4000, bi_modulep = 0xec5000}

create_pagetables 是我们今天探讨的重点。

我们先给出create_pagetables后的内存映射,以期大家能够有总体认识。(接loader内存映像的高端)
      |                |
      |----------------| 0x01025000 physfree
      | vm86paddr 3p   |
      |----------------| 0x01022000
      | vm86phystk 1p  |
      |----------------| 0x01021000
      | proc0kstack 2p |
      |----------------| 0x0101f000
      |  IdlePTD  1p   |
      |----------------| 0x0101e000
      |  KPTphys 30p   |
      |----------------| 0x01000000 KERNend
      |                |
      |----------------| 0xec6000  kernendp
     create_pagetables后的内存映射(其中,30p=30pages=30*4k)

图中,kernendp到KERNend的空白是由于4M对齐造成的。KPTphys代表内核页表(kenel page tables), 占用30个页面,给出的解释是:
#define NKPT        30  /* Enough for 4GB (4MB page tables) */
有些让人费解: 不可能所有的页表都是4M的吧?![FIXME]
IdlePTD代表页目录,proc0kstack是第一个进程的堆栈,接下来两个代码中的解释是:
vm86phystk = 0x1021000  /* vm86/bios stack */ 1p
vm86pa = 0x1022000   /* pgtable + ext + IOPAGES */ 3p
具体分析见《init386分析》
physfree代表可用的物理内存基址,是传给init386的参数。

接下来,几个代码中经常用到的宏:
#define R(foo) ((foo)-KERNBASE)
说明:取foo的实(Real)地址

#define ALLOCPAGES(foo) \
    movl    R(physfree), %esi ; \
    movl    $((foo)*PAGE_SIZE), %eax ; \
    addl    %esi, %eax ; \
    movl    %eax, R(physfree) ; \
    movl    %esi, %edi ; \
    movl    $((foo)*PAGE_SIZE),%ecx ; \
    xorl    %eax,%eax ; \
    cld ; \
    rep ; \
    stosb
说明:从physfree开始分配foo个页面空间,并初始化为0, 并修正physfree的值, 出口处esi保存初始分配地址(即开始physfree的值)

/*
* fillkpt
*  eax = page frame address
*  ebx = index into page table
*  ecx = how many pages to map
*  base = base address of page dir/table
*  prot = protection bits
*/
#define fillkpt(base, prot)       \
    shll    $PTESHIFT,%ebx      ; \
    addl    base,%ebx       ; \
    orl $PG_V,%eax      ; \
    orl prot,%eax       ; \
1:  movl    %eax,(%ebx)     ; \
    addl    $PAGE_SIZE,%eax     ; /* increment physical address */ \
    addl    $PTESIZE,%ebx       ; /* next pte */ \
    loop    1b
说明:把从eax开始的物理地址映射到base的页表,页面属性为prot加上p位,共ecx个页,ebx为index

/*
* fillkptphys(prot)
*  eax = physical address
*  ecx = how many pages to map
*  prot = protection bits
*/
#define fillkptphys(prot)         \
    movl    %eax, %ebx      ; \
    shrl    $PAGE_SHIFT, %ebx   ; \
    fillkpt(R(KPTphys), prot)
说明:把从eax开始的物理地址映射到KPTphys的页表,页面属性为prot加上p位,共ecx个页

接下来,我们开始看代码。页面分配的代码比较简单,我们不做过多说明。请对照create_pagetables后的内存映射阅读。
为了简明,我们没有探讨PAE和SMP相关的代码,只着重分析页表映射建立的代码。

/* Map page zero read-write so bios32 calls can use it */
    xorl    %eax, %eax
    movl    $PG_RW,%edx
    movl    $1,%ecx
    fillkptphys(%edx)
说明:把第0个页面映射到KPTphys,属性为rw。以后我们简写为:
0: rw(1p)
(gdb) x/4x 0x01000000
0x1000000:      0x00000003      0x00000000      0x00000000      0x00000000

/* Map read-only from page 1 to the beginning of the kernel text section */
    movl    $PAGE_SIZE, %eax
    xorl    %edx,%edx
    movl    $R(btext),%ecx
    addl    $PAGE_MASK,%ecx
    subl    %eax,%ecx
    shrl    $PAGE_SHIFT,%ecx
    fillkptphys(%edx)
1 - 0x452: ro  //btext=0xc0452400
(gdb) x/16x 0x01000000
0x1000000:      0x00000003      0x00001001      0x00002001      0x00003001
0x1000010:      0x00004001      0x00005001      0x00006001      0x00007001
0x1000020:      0x00008001      0x00009001      0x0000a001      0x0000b001
0x1000030:      0x0000c001      0x0000d001      0x0000e001      0x0000f001
(gdb) x/8x 0x01001140
0x1001140:      0x00450001      0x00451001      0x00452001      0x00000000
0x1001150:      0x00000000      0x00000000      0x00000000      0x00000000

// btext -- KERNend
    movl    $R(btext),%eax
    addl    $PAGE_MASK, %eax
    andl    $~PAGE_MASK, %eax
    movl    $PG_RW,%edx
    movl    R(KERNend),%ecx
    subl    %eax,%ecx
    shrl    $PAGE_SHIFT,%ecx
    fillkptphys(%edx)
0x453 - 0x1000: rw
(gdb) x/12x 0x01003fe0
0x1003fe0:      0x00ff8003      0x00ff9003      0x00ffa003      0x00ffb003
0x1003ff0:      0x00ffc003      0x00ffd003      0x00ffe003      0x00fff003
0x1004000:      0x00000000      0x00000000      0x00000000      0x00000000

/* Map page directory. */
    movl    R(IdlePTD), %eax
    movl    $NPGPTD, %ecx
    fillkptphys($PG_RW)
0x101e(IdlePTD): rw(1p)
(gdb) x/4x 0x01004074
0x1004074:      0x00000000      0x0101e003      0x00000000      0x00000000

/* Map proc0's KSTACK in the physical way ... */
0x101f - 0x1020: rw(2p) p0kpa
(gdb) x/4x 0x01004078
0x1004078:      0x0101e003      0x0101f003      0x01020003      0x00000000

/* Map ISA hole */0x60p
0xa0 - 0xff: rw
(gdb) x/4x 0x0100027c
0x100027c:      0x0009f001      0x000a0003      0x000a1003      0x000a2003
(gdb) x/4x 0x010003fc
0x10003fc:      0x000ff003      0x00100001      0x00101001      0x00102001

/* Map space for the vm86 region */
0x1021 - 0x1024: rw(4p)
(gdb) x/8x 0x01004080
0x1004080:      0x01020003      0x01021003      0x01022003      0x01023003
0x1004090:      0x01024003      0x00000000      0x00000000      0x00000000

以下映射的基址为vm86pa = 0x1022000
/* Map page 0 into the vm86 page table */
0: rwu(1p)
(gdb) x/4x 0x01021ffc
0x1021ffc:      0x00000000      0x00000007      0x00000000      0x00000000

/* ...likewise for the ISA hole */
0xa0 - 0xff: rwu(0x60p)
(gdb) x/4x 0x0102227c
0x102227c:      0x00000000      0x000a0007      0x000a1007      0x000a2007
(gdb) x/4x 0x010223fc
0x10223fc:      0x000ff007      0x00000000      0x00000000      0x00000000

以下映射的基址为IdlePTD = 0x101e000
/* install a pde for temporary double map of bottom of VA */
0 - 0x1e: rw(30p)
(gdb) x/4x 0x101e000
0x101e000:      0x01000003      0x01001003      0x01002003      0x01003003
(gdb) x/4x 0x101e070
0x101e070:      0x0101c003      0x0101d003      0x00000000      0x00000000

/*
* For the non-PSE case, install PDEs for PTs covering the kernel.
* For the PSE case, do the same, but clobber the ones corresponding
* to the kernel (from btext to KERNend) with 4M ('PS') PDEs immediately
* after.
*/
0x300 - : rw(30p)
(gdb) x/4x 0x101ebfc
0x101ebfc:      0x00000000      0x01000003      0x01001003      0x01002003
(gdb) x/4x 0x101EC6C
0x101ec6c:      0x0101b003      0x0101c003      0x0101d003      0x00000000

pse: 3p
0x300 - 0x302: rw/ps
(gdb) x/4x 0x101ec00
0x101ec00:      0x01000003      0x00400083      0x00800083      0x00c00083
这里就是传说中的3个4MB的页面,覆盖了内核和模块。

done_pde:
/* install a pde recursively mapping page directory as a page table */1p
0x2ff: rw
(gdb) x/4x 0x0101ebf8
0x101ebf8:      0x00000000      0x0101e003      0x01000003      0x00400083
这个pde实现了自身从pd到pt的转换,可以实现虚拟地址到pte的转换,后面我们会经常用它。

最终结果:
(gdb) x/1024x 0x101e000
0x101e000:      0x01000003      0x01001003      0x01002003      0x01003003
0x101e010:      0x01004003      0x01005003      0x01006003      0x01007003
0x101e020:      0x01008003      0x01009003      0x0100a003      0x0100b003
0x101e030:      0x0100c003      0x0100d003      0x0100e003      0x0100f003
0x101e040:      0x01010003      0x01011003      0x01012003      0x01013003
0x101e050:      0x01014003      0x01015003      0x01016003      0x01017003
0x101e060:      0x01018003      0x01019003      0x0101a003      0x0101b003
0x101e070:      0x0101c003      0x0101d003      0x00000000      0x00000000
...............都为0x00000000...........................
0x101ebf0:      0x00000000      0x00000000      0x00000000      0x0101e003
0x101ec00:      0x01000003      0x00400083      0x00800083      0x00c00083
0x101ec10:      0x01004003      0x01005003      0x01006003      0x01007003
0x101ec20:      0x01008003      0x01009003      0x0100a003      0x0100b003
0x101ec30:      0x0100c003      0x0100d003      0x0100e003      0x0100f003
0x101ec40:      0x01010003      0x01011003      0x01012003      0x01013003
0x101ec50:      0x01014003      0x01015003      0x01016003      0x01017003
0x101ec60:      0x01018003      0x01019003      0x0101a003      0x0101b003
0x101ec70:      0x0101c003      0x0101d003      0x00000000      0x00000000
...............都为0x00000000...........................

论坛徽章:
0
发表于 2007-10-14 19:34 |显示全部楼层
我国庆期间写的一篇http://blog.chinaunix.net/u/355/


FreeBSD启动时页表建立过程分析         
        
前几天看 FreeBSD内存管理部分的代码时候发现用户空间的顶部并不是原来想象中的内核空间的起始两者在i386上之间有4M的差异.为此,我在 http://www.freebsdchina.org/forum/viewtopic.php?t=38102 发贴开问.之后通过和NetBSD的代码比较,发现NetBSD里pmap.h中有相关的内容显示了内存空间的分配情况.

/*
* see pte.h for a description of i386 MMU terminology and hardware
* interface.
*
* a pmap describes a processes' 4GB virtual address space.  this
* virtual address space can be broken up into 1024 4MB regions which
* are described by PDEs in the PDP.  the PDEs are defined as follows:
*
* (ranges are inclusive -> exclusive, just like vm_map_entry start/end)
* (the following assumes that KERNBASE is 0xc0000000)
*
* PDE#s    VA range        usage
* 0->766    0x0 -> 0xbfc00000    user address space
* 767        0xbfc00000->        recursive mapping of PDP (used for
*            0xc0000000    linear mapping of PTPs)
* 768->1023    0xc0000000->        kernel address space (constant
*            0xffc00000    across all pmap's/processes)
* 1023        0xffc00000->        "alternate" recursive PDP mapping
*            <end>        (for other pmaps)
*
*
* note: a recursive PDP mapping provides a way to map all the PTEs for
* a 4GB address space into a linear chunk of virtual memory.  in other
* words, the PTE for page 0 is the first int mapped into the 4MB recursive
* area.  the PTE for page 1 is the second int.  the very last int in the
* 4MB range is the PTE that maps VA 0xfffff000 (the last page in a 4GB
* address).
*
* all pmap's PD's must have the same values in slots 768->1023 so that
* the kernel is always mapped in every process.  these values are loaded
* into the PD at pmap creation time.
*
* at any one time only one pmap can be active on a processor.  this is
* the pmap whose PDP is pointed to by processor register %cr3.  this pmap
* will have all its PTEs mapped into memory at the recursive mapping
* point (slot #767 as show above).  when the pmap code wants to find the
* PTE for a virtual address, all it has to do is the following:
*
* address of PTE = (767 * 4MB) + (VA / PAGE_SIZE) * sizeof(pt_entry_t)
*                = 0xbfc00000 + (VA / 4096) * 4
*
* what happens if the pmap layer is asked to perform an operation
* on a pmap that is not the one which is currently active?  in that
* case we take the PA of the PDP of non-active pmap and put it in
* slot 1023 of the active pmap.  this causes the non-active pmap's
* PTEs to get mapped in the final 4MB of the 4GB address space
* (e.g. starting at 0xffc00000).
*
* the following figure shows the effects of the recursive PDP mapping:
*
*   PDP (%cr3)
*   +----+
*   |   0| -> PTP#0 that maps VA 0x0 -> 0x400000
*   |    |
*   |    |
*   | 767| -> points back to PDP (%cr3) mapping VA 0xbfc00000 -> 0xc0000000
*   | 768| -> first kernel PTP (maps 0xc0000000 -> 0xc0400000)
*   |    |
*   |1023| -> points to alternate pmap's PDP (maps 0xffc00000 -> end)
*   +----+
*
* note that the PDE#767 VA (0xbfc00000) is defined as "PTE_BASE"
* note that the PDE#1023 VA (0xffc00000) is defined as "APTE_BASE"
*
* starting at VA 0xbfc00000 the current active PDP (%cr3) acts as a
* PTP:
*
* PTP#767 == PDP(%cr3) => maps VA 0xbfc00000 -> 0xc0000000
*   +----+
*   |   0| -> maps the contents of PTP#0 at VA 0xbfc00000->0xbfc01000
*   |    |
*   |    |
*   | 767| -> maps contents of PTP#767 (the PDP) at VA 0xbfeff000
*   | 768| -> maps contents of first kernel PTP
*   |    |
*   |1023|
*   +----+
*
* note that mapping of the PDP at PTP#767's VA (0xbfeff000) is
* defined as "PDP_BASE".... within that mapping there are two
* defines:
*   "PDP_PDE" (0xbfeffbfc) is the VA of the PDE in the PDP
*      which points back to itself.
*   "APDP_PDE" (0xbfeffffc) is the VA of the PDE in the PDP which
*      establishes the recursive mapping of the alternate pmap.
*      to set the alternate PDP, one just has to put the correct
*    PA info in *APDP_PDE.
*
* note that in the APTE_BASE space, the APDP appears at VA
* "APDP_BASE" (0xfffff000).
*/

    从上面的信息看来4G的线性地址空间里,用户空间是从0x00000000 -> 0xbfc00000 也就是3G-4M.这4M空间实际上是用来存放页表的.这部分代码一部分在locore.S里,还有一部分在pmap.c里.

    下面来看看locore.S.它是内核最先被执行的代码.内核被bootloader加载后就跳转到locore.S开始执行.其中 create_pagetables开始的一段代码(应该说是一个汇编实现的函数)是我所感兴趣的部分.内核在检测完CPU类型后就开始建立页表.通过之后的源代码分析,此时的CPU应该是被bootloader设置为了保护模式但是还没有开启分页.建立完页表之后就可以开启分页机制了.为了简单起见,我略过了SMP,PAE相关的代码.

706 /**********************************************************************
707  *
708  * Create the first page directory and its page tables.
709  *
710  */
711
712 create_pagetables:
713
714 /* Find end of kernel image (rounded up to a page boundary). */
715         movl    $R(_end),%esi
716
717 /* Include symbols, if any. */
718         movl    R(bootinfo+BI_ESYMTAB),%edi
719         testl   %edi,%edi
720         je      over_symalloc
721         movl    %edi,%esi
722         movl    $KERNBASE,%edi
723         addl    %edi,R(bootinfo+BI_SYMTAB)
724         addl    %edi,R(bootinfo+BI_ESYMTAB)
725 over_symalloc:
726
727 /* If we are told where the end of the kernel space is, believe it. */
728         movl    R(bootinfo+BI_KERNEND),%edi
729         testl   %edi,%edi
730         je      no_kernend
731         movl    %edi,%esi
732 no_kernend:
733
734         addl    $PDRMASK,%esi           /* Play conservative for now, and */
735         andl    $~PDRMASK,%esi          /*   ... wrap to next 4M. */
736         movl    %esi,R(KERNend)         /* save end of kernel */
737         movl    %esi,R(physfree)        /* next free page is at end of kernel */

    首先确定内核加载之后,内核的尾端在哪里.然后以4M对齐,把尾地址写入KERNend和physfree.以4M对齐的目的是为了建立页目录的时候, 内核映像部分能够正好凑齐整数个页目录(页目录里每项可寻址4M空间即1024个页表项*4096字节/页).现在内核的尾地址被存放在了KERNend 里.而physfree里面存放的是空闲物理内存的起始地址,最初它和KERNend是一样的.



    继续往下分析前,我们看两个宏定义

156 #define R(foo) ((foo)-KERNBASE)/* 用来把线性地址转化为物理地址,由于链接的时候是使用KERNBASE作为ELF文件的基址,所以代码里所有的全局变量的地址、函数地址都是以 KERNBASE为基址的,而bootloader是把内核加载到物理地址0开始的空间里,这个宏就是用来把线性地址转化为内核在内存中的实际的物理地址 */
157
158 #define ALLOCPAGES(foo) \                                                   
159         movl    R(physfree), %esi ;     /* esi = 空闲页面地址 */        \   
160         movl    $((foo)*PAGE_SIZE), %eax ; /* 计算出需要的字节数 */     \   
161         addl    %esi, %eax ;                                            \   
162         movl    %eax, R(physfree) ;     /* 更新空闲页面地址 */          \   
163         movl    %esi, %edi ;            /* 分配到的空间首地址 */        \   
164         movl    $((foo)*PAGE_SIZE),%ecx ;                               \   
165         xorl    %eax,%eax ;                                             \   
166         cld ;                                                           \   
167         rep ;                           /* 清空分配的内存 */            \   
168         stosb                                                               
169

    通过ALLOCPAGES的定义可以知道,ALLOCPAGES的参数是需要申请的内存页数,申请到的内存起始地址放在esi寄存器里.

    OK,可以继续分析了。
739 /* Allocate Kernel Page Tables */
740         ALLOCPAGES(NKPT)
741         movl    %esi,R(KPTphys)
742
        .
        .
        .
        .
749         ALLOCPAGES(NPGPTD)
750         movl    %esi,R(IdlePTD)
751
752 /* Allocate KSTACK */
753         ALLOCPAGES(KSTACK_PAGES)
754         movl    %esi,R(p0kpa)
755         addl    $KERNBASE, %esi
756         movl    %esi, R(proc0kstack)
757
758         ALLOCPAGES(1)                   /* vm86/bios stack */
759         movl    %esi,R(vm86phystk)
760
761         ALLOCPAGES(3)                   /* pgtable + ext + IOPAGES */
762         movl    %esi,R(vm86pa)
763         addl    $KERNBASE, %esi
764         movl    %esi, R(vm86paddr)

        .
        .
        .
        .


    根据注释可知,分配了NKPT个页面给KPTphys用来之后存放内核页表,分配了NPGPTD个页面给IdlePTD用来存放页目录。其他剩下的几个分配的都跟我要知道的内容无关,就直接忽略。
    继续前再看两个宏。
170 /*
171  * fillkpt
172  *      eax = page frame address
173  *      ebx = index into page table
174  *      ecx = how many pages to map
175  *      base = base address of page dir/table
176  *      prot = protection bits
177  */
178 #define fillkpt(base, prot)               \
179         shll    $PTESHIFT,%ebx          ; \
180         addl    base,%ebx               ; \
181         orl     $PG_V,%eax              ; \
182         orl     prot,%eax               ; \
183 1:      movl    %eax,(%ebx)             ; \
184         addl    $PAGE_SIZE,%eax         ; /* increment physical address */ \
185         addl    $PTESIZE,%ebx           ; /* next pte */ \
186         loop    1b
187
188 /*
189  * fillkptphys(prot)
190  *      eax = physical address
191  *      ecx = how many pages to map
192  *      prot = protection bits
193  */
194 #define fillkptphys(prot)                 \
195         movl    %eax, %ebx              ; \
196         shrl    $PAGE_SHIFT, %ebx       ; \
197         fillkpt(R(KPTphys), prot)

    fillkpt是为从eax开始的ecx个页面建立对应的页表,页表存放在base开始的第ebx项PTE(base+ebx*4)
    fillptphys是给从eax开始的ecx页面在KPTphys区域里建立页表,偏移也是由eax相对0计算来的.

    OK继续往下走.到了建立一堆页表的时候了.

814         xorl    %eax, %eax
815         movl    R(KERNend),%ecx
816         shrl    $PAGE_SHIFT,%ecx
817         fillkptphys($PG_RW)
819 /* Map page directory. */
        .
        .
        .
        .
825
826         movl    R(IdlePTD), %eax
827         movl    $NPGPTD, %ecx
828         fillkptphys($PG_RW)/* Map page directory. */
        .
        .
        .
        .

    上面这两段代码就是给内核镜像和存放页目录的页(IdlePTD)在KPTphys里建立页表.
    现在页表已经建立好了,只要把页表所在的几个页的首地址存到页目录里就可以开启分页机制爽了.
   
    最后一段:
901         movl    R(KPTphys), %eax
902         xorl    %ebx, %ebx
903         movl    $NKPT, %ecx
904         fillkpt(R(IdlePTD), $PG_RW)

        .
        .
        .
        .
920 /*
921  * For the non-PSE case, install PDEs for PTs covering the KVA.
922  * For the PSE case, do the same, but clobber the ones corresponding
923  * to the kernel (from btext to KERNend) with 4M (2M for PAE) ('PS')
924  * PDEs immediately after.
925  */
926         movl    R(KPTphys), %eax
927         movl    $KPTDI, %ebx
928         movl    $NKPT, %ecx
929         fillkpt(R(IdlePTD), $PG_RW)

        .
        .
        .省略对PSE开启时的处理
        .

946 done_pde:
947 /* install a pde recursively mapping page directory as a page table */
948         movl    R(IdlePTD), %eax
949         movl    $PTDPTDI, %ebx
950         movl    $NPGPTD,%ecx
951         fillkpt(R(IdlePTD), $PG_RW)
   
    901~904是给KPTPhys这个存放页表的区域的页面建立页目录,注意它存放在了IdlePTD的最顶端,之后926~929又在IdlePTD +KPTDI*4处建立了一份相同的页目录项.948~951是在IdlePTD+PTDPTDI*4处给IdlePTD自己建立页目录.之所以给自己建个页目录项,是为了把所有存放页表的区域集合到连续的线性地址空间上来.因为本身页目录里面放的也是物理地址,也可以当成页表用.

    建立页面之后的代码就是将IdlePTD赋值给CR3寄存器.这样一来我们的线性地址空间分布就可以得到了.由于线性地址空间的分布主要是由在页目录中的偏移决定的.我们看看页目录里现在有哪些东西.由于在i386平台上目前KPTDI=768,PTDPTDI=767,可以得出KPTphys里面页表指向的页面既可以直接使用物理地址(因为页目录里第0项就是指向KPTphys那一堆页表),也可以使用KPTDI<<22+物理地址(页目录第KPTDI项开始也是指向KPTPhys).也就是说物理地址0~(KERNend-KERNBASE)被映射到了KERNBASE~KERNend 上.但是存放在IdlePTD最前面的那些页目录项是临时工,在pmap_bootstrap里会被清除掉.而由于第PTDPTDI项是指向的页目录自己所以KPTDI<<22之前的4M就是放的描述4G空间的页表,总共1M项共4M空间.因此0xc0000000以前的4M空间就是放页表用的.



[ 本帖最后由 lllaaa 于 2007-10-14 19:36 编辑 ]

评分

参与人数 1可用积分 +5 收起 理由
gvim + 5 好文

查看全部评分

论坛徽章:
0
发表于 2007-10-14 23:28 |显示全部楼层
好文,收藏了!

论坛徽章:
0
发表于 2007-10-15 13:57 |显示全部楼层
原帖由 lllaaa 于 2007-10-14 19:34 发表
我国庆期间写的一篇http://blog.chinaunix.net/u/355/


FreeBSD启动时页表建立过程分析         
        
前几天看 FreeBSD内存管理部分的代码时候发现用户空间的顶部并不是原来想象中的内核空间的起始两 ...


你比我写的好多了,看来我是班门弄斧了!
另外看你的blog你阅读了BSD TCP/IP协议栈的代码,我也想看呢,有时间写出来让大家拜读一下吧,呵呵!

论坛徽章:
0
发表于 2007-10-15 23:25 |显示全部楼层

回复 #4 qiuhanty 的帖子

最近对内存管理比较感兴趣,呵呵.

协议栈最近没时间看了.

工作上也用不到了.

之前在做核心路由器流量管理这一块,现在业务调整,改做交换机了.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

SACC2019中国系统架构师大会

【数字转型 架构演进】SACC2019中国系统架构师大会,8.5折限时优惠重磅来袭!
2019年10月31日~11月2日第11届中国系统架构师大会(SACC2019)将在北京隆重召开。四大主线并行的演讲模式,1个主会场、20个技术专场、超千人参与的会议规模,100+来自互联网、金融、制造业、电商等领域的嘉宾阵容,将为广大参会者提供一场最具价值的技术交流盛会。

限时8.5折扣期:2019年9月30日前


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

大会官网>>
  

北京盛拓优讯信息技术有限公司. 版权所有 16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122
中国互联网协会会员  联系我们:huangweiwei@it168.com
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP