linux 内存管理---分页系统之页表初始化(四)

arch_mem_init()在调用bootmem_init()函数完成bootmem的初始化之后调用paging_init()进行分页系统的初始化。
分页系统就是将物理内存按页为单位进行描述,管理和映射。
注意这里是mips cpu,不同的arch cpu初始化顺序可能不同, linux-3.3.8
[arch/mips/mm/init.c]
void __init paging_init(void)
{
    unsigned long max_zone_pfns[MAX_NR_ZONES];
    unsigned long lastpfn __maybe_unused;

    pagetable_init();          //页表初始化

#ifdef CONFIG_HIGHMEM
    kmap_init();
#endif
    kmap_coherent_init();

#ifdef CONFIG_ZONE_DMA
    max_zone_pfns[ZONE_DMA] = MAX_DMA_PFN;
#endif
#ifdef CONFIG_ZONE_DMA32
    max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN;
#endif
    max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
    lastpfn = max_low_pfn;
#ifdef CONFIG_HIGHMEM
    max_zone_pfns[ZONE_HIGHMEM] = highend_pfn;
    lastpfn = highend_pfn;

    if (cpu_has_dc_aliases && max_low_pfn != highend_pfn) {
        printk(KERN_WARNING "This processor doesn't support highmem."
               " %ldk highmem ignored\n",
               (highend_pfn - max_low_pfn) << (PAGE_SHIFT - 10));
        max_zone_pfns[ZONE_HIGHMEM] = max_low_pfn;
        lastpfn = max_low_pfn;
    }
#endif

    free_area_init_nodes(max_zone_pfns); 
}
初始化页表
[arch/mips/mm/pgtable-32.c]
void __init pagetable_init(void)
{
    unsigned long vaddr;
    pgd_t *pgd_base;
#ifdef CONFIG_HIGHMEM
    pgd_t *pgd;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
#endif

    /* Initialize the entire pgd.  */
    pgd_init((unsigned long)swapper_pg_dir);            // pgd_t swapper_pg_dir[_PTRS_PER_PGD] __page_aligned(_PGD_ORDER);
                                                                                  //  #define _PTRS_PER_PGD 1024 /* PTRS_PER_PGD         # */        2^10=1024个pgdir项
                                                                                  // typedef struct { unsigned long pgd; } pgd_t;            每个pgdir项占用4byte
                                                                                  //初始化用户态地址空间 0-0x7FFFFFFFUL 对应的pgdir项
    pgd_init((unsigned long)swapper_pg_dir              //初始化内核态地址空间0x80000000UL-0xFFFFFFFF对应的pgdir项
         + sizeof(pgd_t) * USER_PTRS_PER_PGD);

    pgd_base = swapper_pg_dir;

    /*
     * Fixed mappings:                    //建立固定映射占用的虚拟地址空间对应的页表项
     */
    vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
    fixrange_init(vaddr, vaddr + FIXADDR_SIZE, pgd_base);

#ifdef CONFIG_HIGHMEM
    /*
     * Permanent kmaps:
     */
    vaddr = PKMAP_BASE;                         //建立永久映射占用的虚拟地址空间对应的页表项
    fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);

    pgd = swapper_pg_dir + __pgd_offset(vaddr);
    pud = pud_offset(pgd, vaddr);
    pmd = pmd_offset(pud, vaddr);
    pte = pte_offset_kernel(pmd, vaddr);
    pkmap_page_table = pte;
#endif
}

#ifdef CONFIG_PAGE_SIZE_4KB
#define PAGE_SHIFT    12
#endif

#define PGDIR_SHIFT    (2 * PAGE_SHIFT + PTE_ORDER - PTE_T_LOG2)        //2*12-0-2=22,也就是说page gobal directory占用32-22=10bit
#define PGDIR_SIZE    (1UL << PGDIR_SHIFT)                                                //所以一个pgdir项对应的内存大小为1<<22

#define USER_PTRS_PER_PGD    (0x80000000UL/ PGDIR_SIZE)                      //整个用户地址空间0-0x7FFFFFFFUL对应的pgd ir项个

void pgd_init(unsigned long page)
{
    unsigned long *p = (unsigned long *) page;
    int i;

    for (i = 0; i < USER_PTRS_PER_PGD; i+=8) {
        p[i + 0] = (unsigned long) invalid_pte_table;
        p[i + 1] = (unsigned long) invalid_pte_table;
        p[i + 2] = (unsigned long) invalid_pte_table;
        p[i + 3] = (unsigned long) invalid_pte_table;
        p[i + 4] = (unsigned long) invalid_pte_table;
        p[i + 5] = (unsigned long) invalid_pte_table;
        p[i + 6] = (unsigned long) invalid_pte_table;
        p[i + 7] = (unsigned long) invalid_pte_table;
    }
}

void __init fixrange_init(unsigned long start, unsigned long end,
    pgd_t *pgd_base)
{
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_MIPS_MT_SMTC)
    pgd_t *pgd;
    pud_t *pud;
    pmd_t *pmd;
    pte_t *pte;
    int i, j, k;
    unsigned long vaddr;

    vaddr = start;
    i = __pgd_offset(vaddr);
    j = __pud_offset(vaddr);
    k = __pmd_offset(vaddr);
    pgd = pgd_base + i;

    for ( ; (i < PTRS_PER_PGD) && (vaddr < end); pgd++, i++) {
        pud = (pud_t *)pgd;
        for ( ; (j < PTRS_PER_PUD) && (vaddr < end); pud++, j++) {
            pmd = (pmd_t *)pud;
            for (; (k < PTRS_PER_PMD) && (vaddr < end); pmd++, k++) {
                if (pmd_none(*pmd)) {
                    pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
                    set_pmd(pmd, __pmd((unsigned long)pte));
                    BUG_ON(pte != pte_offset_kernel(pmd, 0));
                }
                vaddr += PMD_SIZE;
            }
            k = 0;
        }
        j = 0;
    }
#endif
}

[arch/mips/include/asm/pgtable.h]
#ifdef CONFIG_32BIT
#include <asm/pgtable-32.h>
#endif
#ifdef CONFIG_64BIT
#include <asm/pgtable-64.h>
#endif

#define __pgd_offset(address)    pgd_index(address)
#define pgd_index(address)    (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1))              / /PGDIR_SHIFT=22, 右移22bit,即取最高10bit,得到page gobal directory  index

#define __pud_offset(address)    (((address) >> PUD_SHIFT) & (PTRS_PER_PUD-1))          / /#define PTRS_PER_PUD    1, 得到page upper directory,这里pud显然不存在
#define __pmd_offset(address)    (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))        //#define PTRS_PER_PMD    1
这里是 10bit pgd + 10bit pmd +12bit offset

init_mm.pgd----------> +--------------+   4k               +----------→ +--------------+   4k            +---------→--------+  4k
                                       |   pgd(4byte) |                      |                      |   pte(4byte)  |                    |                              |        
                                      +--------------+                     |                     +--------------+                   |                              |        
                                       |         ....         |    1024项       |                      |         ....          |    1024项    |                              |        
                                      +------------- +                     |                     +------------- -+                  |                               |       
     offset    +--------→   |   pgd(4byte) |  -------------+          +---→ |   pte(4byte)   |  -----------+         +---------→  |       
                   |                  +--------------+                                 |         +---------------+                             |                   +----
                   |                                                                             |                                                                |                    
                   |                                                                             |                                                                |
    10bit ---+                                                              10bit ---+                                          12bit--------+

高端内存
Documentation/vm/highmem.txt中关于高端内存的介绍:
High memory (highmem) is used when the size of physical memory approaches or exceeds the maximum size of virtual memory.  At that point it becomes impossible for the kernel to keep all of the available physical memory mapped at all times.This means the kernel needs to start using temporary mappings of the pieces of physical memory that it wants to access.

The traditional split for architectures using this approach is 3:1, 3GiB for
userspace and the top 1GiB for kernel space:

        +--------+ 0xffffffff
        | Kernel   |
        +--------+ 0xc0000000
        |              |
        | User      |
        |              |
        +--------+ 0x00000000

This means that the kernel can at most map 1GiB of physical memory at any one time, but because we need virtual address space for other things - including temporary maps to access the rest of the physical memory - the actual direct  map will typically be less (usually around ~896MiB).

对应mips cpu,2G用于内核地址空间:
        +--------+ 0xffffffff
        |              |
        | Kernel   |
        +--------+ 0x80000000
        | User     |
        |             |
       +--------+ 0x00000000

obj-$(CONFIG_HIGHMEM)                += highmem.o

min_low_pfn:最小低端内存
max_low_pfn:最大低端内存
max_pfn: 最大物理页框
highstart_pfn:高端内存其实页框
highend_pfn:高端内存结束页框
max_mapnr:最大映射页框
#ifdef CONFIG_HIGHMEM
#ifdef CONFIG_DISCONTIGMEM
#error "CONFIG_HIGHMEM and CONFIG_DISCONTIGMEM dont work together yet"
#endif
    max_mapnr = highend_pfn ? highend_pfn : max_low_pfn;
#else
    max_mapnr = max_low_pfn;
#endif
如果CONFIG_HIGHMEM选项不打开,max_mapnr = max_low_pfn,则HIGHMEM_START以上的物理内存将不能访问,这样就会造成RAM浪费。


Fix-mapped linear addresses(简称Fixed mappings)包括temporary kernel mappings 和coherent kmap
temporary kernel mappings建立映射:void *__kmap_atomic(struct page *page)
temporary kernel mappings撤销映射:void __kunmap_atomic(void *kvaddr)
temporary kernel mappings不可阻塞


coherent kmap建立映射:void *kmap_coherent(struct page *page, unsigned long addr)
coherent kmap撤销映射:void kunmap_coherent(void)

persistent kernel mappings(简称Permanent kmaps)建立映射:void *kmap(struct page *page)
persistent kernel mappings撤销映射:void kunmap(struct page *page)
persistent kernel mappings可阻塞


#define HIGHMEM_START        (UPPERMEM_START + (BRCM_MAX_UPPER_MB << 20))
#define UPPERMEM_START        _AC(0x20000000, UL)
如果BRCM_HAS_XKS01不支持,即kseg0只支持512M
#define BRCM_MAX_UPPER_MB    _AC(0, UL), 则 HIGHMEM_START 从  0x20000000 即512M就算高端内存
#define MAP_BASE        _AC(0xc0000000, UL) , 此时内核态MMU映射从0xc0000000开始
如果BRCM_HAS_XKS01支持,即kseg0支持达到1024M
#define BRCM_MAX_UPPER_MB    _AC(768, UL) , 则 HIGHMEM_START从0x20000000+768M 即512M+768M开始算高端内存
#define MAP_BASE        _AC(0xe0000000, UL) , 此时内核态MMU映射从0xe0000000开始


void __init kmap_init(void)
{
    unsigned long kmap_vstart;

    /* cache the first kmap pte */
    kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
    kmap_pte = kmap_get_fixmap_pte(kmap_vstart);     //起始fix map 对应的pte
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值