Linux 内核学习之内存寻址(二) Linux内存寻址

Linux分段

Linux以非常有限的方式使用分段。2.6 版本的Linux只有x86结构才需要分段。
四个主要的Linux段:

BaseGLimitSTypeDPLD/BP
用户代码段0x0000000010xfffff110311
用户数据段0x0000000010xfffff12311
内核代码段0x0000000010xfffff110011
内核数据段0x0000000010xfffff12011

所有段都是从0x00000000开始,说明Linux逻辑地址和线性地址是一致的,即偏移量字段和相关线性地址是一致的。
CPL反应了进程是在用户态还是内核态。

GDT
每个CPU都有一个全局描述符表(GDT), 包含18个段描述符和14个空的。
全局描述符表

LDT
大多数用户态下的Linux程序不使用局部描述符,内核定义了一个缺省的LDT供大多数进程使用。

Linux分页

2.6.11 版本后Linux采用四级分页(为了兼容)

  • 页全局目录 Page Global Directory
  • 页上级目录 Page Upper Directory
  • 页中间级目录 Page Middle Directory
  • 页表 Page Table

Linux分页

分几种情况
1. 32位系统无PAE,二级目录 页全局+页表+offset;
2. 32位系统带PAE,三级页全局+中间级目录+页表+offset;
3. 64位系统采用三级还是四级依赖于对线性地址的划分。
注:开启大页(4MB or 2MB)的话,页表和offset合并为offset.
64为系统分页

内核页表的建立

物理内存布局
在初始化阶段,内核必须建立一个物理地址来指定哪些物理地址对内核可用而哪些不可用。
一般来说,Linux内核安装在RAM中从物理地址0x0010 0000开始的地方,也就是说从第二MB开始。典型的配置所得到的内核可以安装在小于3MB的RAM中。(这么做是因为PC体系结构,RAM的前1MB留供BIOS使用)。
物理布局

因为内核刚被载入到RAM当中,CPU仍处于实模式,分页功能还没开启。因此内核初始化自己的页框,分为两个阶段。
1. 内核创建一个有限的地址空间,包括内核的代码段和数据段、初始页表和用于存放动态数据结构的共128KB大小的空间。这个空间只够内核装入RAM和对其初始化的核心数据结构。
2. 内核充分利用剩余的RAM并适当建立分页页表。

下面代码是内核初始化页表的源代码(Linux kernel 4.0)。为了能在保护模式和实模式下都能寻址,页目录项的0 和768, 1 和769 …. 指向同一页表(未开启PAE)。

#if PTRS_PER_PMD > 1
#define PAGE_TABLE_SIZE(pages) (((pages) / PTRS_PER_PMD) + PTRS_PER_PGD)
#else
#define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD)
#endif

/*
 * Number of possible pages in the lowmem region.
 *
 * We shift 2 by 31 instead of 1 by 32 to the left in order to avoid a
 * gas warning about overflowing shift count when gas has been compiled
 * with only a host target support using a 32-bit type for internal
 * representation.
 */
LOWMEM_PAGES = (((2<<31) - __PAGE_OFFSET) >> PAGE_SHIFT)
MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT
/* Enough space to fit pagetables for the low memory linear map */
MAPPING_BEYOND_END = PAGE_TABLE_SIZE(LOWMEM_PAGES) << PAGE_SHIFT

可以算出 MAPPING_BEYOND_END = 0x 10 0000 // 1MB

/*
 * Initialize page tables.  This creates a PDE and a set of page
 * tables, which are located immediately beyond __brk_base.  The variable
 * _brk_end is set up to point to the first "safe" location.
 * Mappings are created both at virtual address 0 (identity mapping)
 * and PAGE_OFFSET for up to _end.
 */
#ifdef CONFIG_X86_PAE

    /*
     * In PAE mode initial_page_table is statically defined to contain
     * enough entries to cover the VMSPLIT option (that is the top 1, 2 or 3
     * entries). The identity mapping is handled by pointing two PGD entries
     * to the first kernel PMD.
     *
     * Note the upper half of each PMD or PTE are always zero at this stage.
     */

#define KPMDS (((-__PAGE_OFFSET) >> 30) & 3) /* Number of kernel PMDs */

    xorl %ebx,%ebx              /* %ebx is kept at zero */

    movl $pa(__brk_base), %edi
    movl $pa(initial_pg_pmd), %edx
    movl $PTE_IDENT_ATTR, %eax
10:
    leal PDE_IDENT_ATTR(%edi),%ecx      /* Create PMD entry */
    movl %ecx,(%edx)            /* Store PMD entry */
                        /* Upper half already zero */
    addl $8,%edx
    movl $512,%ecx
11:
    stosl
    xchgl %eax,%ebx
    stosl
    xchgl %eax,%ebx
    addl $0x1000,%eax
    loop 11b

    /*
     * End condition: we must map up to the end + MAPPING_BEYOND_END.
     */
    movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
    cmpl %ebp,%eax
    jb 10b
1:
    addl $__PAGE_OFFSET, %edi
    movl %edi, pa(_brk_end)
    shrl $12, %eax
    movl %eax, pa(max_pfn_mapped)

    /* Do early initialization of the fixmap area */
    movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
    movl %eax,pa(initial_pg_pmd+0x1000*KPMDS-8)
#else   /* Not PAE */

page_pde_offset = (__PAGE_OFFSET >> 20);  // 0xc0000000 >> 20 ???

    movl $pa(__brk_base), %edi   //__brk_base??? /*__brk_base==PTD*/  // pg0的地址 __end
    movl $pa(initial_page_table), %edx             //initial_page_table 和 __brk_base 不再一个位置
    movl $PTE_IDENT_ATTR, %eax    //???
10:
    leal PDE_IDENT_ATTR(%edi),%ecx      /* Create PDE entry */ #define PDE_IDENT_ATTR    0x063      /* PRESENT+RW+DIRTY+ACCESSED */ 页目录项
    movl %ecx,(%edx)            /* Store identity PDE entry */
    movl %ecx,page_pde_offset(%edx)     /* Store kernel PDE entry */  // ?? 0xc00 / 4 = 0x300 -> 768   实模式和保护模式指向同一页表
    addl $4,%edx                        // next page ??
    movl $1024, %ecx               //循环 1024次 建立1024个表项
11:                                 //初始化页表 1024项
    stosl                          ///eax的内容放入 edi指向的物理地址 edi+=4     
    addl $0x1000,%eax              // 所有项都填0 /*eax: 0x1063, 0x2063, 0x3063 …, 0x3ff063*/  /* PRESENT+RW+DIRTY+ACCESSED */
    loop 11b                       // 内循环填充表目录  
    /*
     * End condition: we must map up to the end + MAPPING_BEYOND_END.
     */
    movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp     //这个地方不太明白。。。 1MB
    cmpl %ebp,%eax       ebp >= eax jump 继续映射下个目录项
    jb 10b
    addl $__PAGE_OFFSET, %edi   // + 0xc000 0000 线性地址
    movl %edi, pa(_brk_end)    // 存入 brk 末端
    shrl $12, %eax             // 右移12位  最终页表地址--> 得到目录项
    movl %eax, pa(max_pfn_mapped)  //计算最大页数 max_pfn_mapped

    /* Do early initialization of the fixmap area */   //高端映射??
    movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
    movl %eax,pa(initial_page_table+0xffc)                 //目录最后一项 保存 initial_pag_fixmap 表项  固定映射??
#endif
#endif

内核编译后的可执行代码中的地址都是虚拟地址,也就是说地址都大于0xc0000000, 目标是要内核映象在虚拟内核空间中运行。在 分页机制开启前这些地址是无效的。不能直接被送到cpu的外部地址总线上,用于直接寻址对应的物理内存。

enable_paging:

/*
 * Enable paging
 */
    movl $pa(initial_page_table), %eax
    movl %eax,%cr3      /* set the page table pointer.. */
    movl $CR0_STATE,%eax
    movl %eax,%cr0      /* ..and set paging (PG) bit */
    ljmp $__BOOT_CS,$1f    /* Clear prefetch and normalize %eip */
1:
    /* Shift the stack pointer to a virtual address */
    addl $__PAGE_OFFSET, %esp
/*
 * BSS section    /***bss 区**/
 */
__PAGE_ALIGNED_BSS
    .align PAGE_SIZE
#ifdef CONFIG_X86_PAE
initial_pg_pmd:
    .fill 1024*KPMDS,4,0
#else
ENTRY(initial_page_table)
    .fill 1024,4,0
#endif
initial_pg_fixmap:
    .fill 1024,4,0
ENTRY(empty_zero_page)
    .fill 4096,1,0
ENTRY(swapper_pg_dir)
    .fill 1024,4,0
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值