现在的位置: 首页 > 综合 > 正文

第二次启动分页管理

2013年05月11日 ⁄ 综合 ⁄ 共 6873字 ⁄ 字号 评论关闭

5.2.4 第二次启动分页管理

回到init_memory_mapping()函数,之后nr_range次调用kernel_physical_mapping_init()nr_rangemap_range数组的总的元素数,前面总共执行了两次save_mr,所以nr_range2,分别是0~1<<92MB~end>>12map_range结构,代表4k2MB两种形式的内存页表。不过为了方便起见,我们暂时忽略2MB页面大小的体系,只分析传统4k页面体系。

 

238unsigned long __init

 239kernel_physical_mapping_init(unsigned long start,

 240                             unsigned long end,

 241                             unsigned long page_size_mask)

 242{

 243        int use_pse = page_size_mask == (1<<PG_LEVEL_2M);

 244        unsigned long last_map_addr = end;

 245        unsigned long start_pfn, end_pfn;

 246        pgd_t *pgd_base = swapper_pg_dir;

 247        int pgd_idx, pmd_idx, pte_ofs;

 248        unsigned long pfn;

 249        pgd_t *pgd;

 250        pmd_t *pmd;

 251        pte_t *pte;

 252        unsigned pages_2m, pages_4k;

 253        int mapping_iter;

 254

 255        start_pfn = start >> PAGE_SHIFT;

 256        end_pfn = end >> PAGE_SHIFT;

 257

 258        /*

 259         * First iteration will setup identity mapping using large/small pages

 260         * based on use_pse, with other attributes same as set by

 261         * the early code in head_32.S

 262         *

 263         * Second iteration will setup the appropriate attributes (NX, GLOBAL..)

 264         * as desired for the kernel identity mapping.

 265         *

 266         * This two pass mechanism conforms to the TLB app note which says:

 267         *

 268         *     "Software should not write to a paging-structure entry in a way

 269         *      that would change, for any linear address, both the page size

 270         *      and either the page frame or attributes."

 271         */

 272        mapping_iter = 1;

 273

 274        if (!cpu_has_pse)

 275                use_pse = 0;

 276

 277repeat:

 278        pages_2m = pages_4k = 0;

 279        pfn = start_pfn;

 280        pgd_idx = pgd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET);

 281        pgd = pgd_base + pgd_idx;

 282        for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {

 283                pmd = one_md_table_init(pgd);

 284

 285                if (pfn >= end_pfn)

 286                        continue;

 287#ifdef CONFIG_X86_PAE

 288                pmd_idx = pmd_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET);

 289                pmd += pmd_idx;

 290#else

 291                pmd_idx = 0;

 292#endif

 293                for (; pmd_idx < PTRS_PER_PMD && pfn < end_pfn;

 294                     pmd++, pmd_idx++) {

 295                        unsigned int addr = pfn * PAGE_SIZE + PAGE_OFFSET;

 296

 297                        /*

 298                         * Map with big pages if possible, otherwise

 299                         * create normal page tables:

 300                         */

 301                        if (use_pse) {

 302                                unsigned int addr2;

 303                                pgprot_t prot = PAGE_KERNEL_LARGE;

 304                                /*

 305                                 * first pass will use the same initial

 306                                 * identity mapping attribute + _PAGE_PSE.

 307                                 */

 308                                pgprot_t init_prot =

 309                                        __pgprot(PTE_IDENT_ATTR |

 310                                                 _PAGE_PSE);

 311

 312                                addr2 = (pfn + PTRS_PER_PTE-1) * PAGE_SIZE +

 313                                        PAGE_OFFSET + PAGE_SIZE-1;

 314

 315                                if (is_kernel_text(addr) ||

 316                                    is_kernel_text(addr2))

 317                                        prot = PAGE_KERNEL_LARGE_EXEC;

 318

 319                                pages_2m++;

 320                                if (mapping_iter == 1)

 321                                        set_pmd(pmd, pfn_pmd(pfn, init_prot));

 322                                else

 323                                        set_pmd(pmd, pfn_pmd(pfn, prot));

 324

 325                                pfn += PTRS_PER_PTE;

 326                                continue;

 327                        }

 328                        pte = one_page_table_init(pmd);

 329

 330                        pte_ofs = pte_index((pfn<<PAGE_SHIFT) + PAGE_OFFSET);

 331                        pte += pte_ofs;

 332                        for (; pte_ofs < PTRS_PER_PTE && pfn < end_pfn;

 333                             pte++, pfn++, pte_ofs++, addr += PAGE_SIZE) {

 334                                pgprot_t prot = PAGE_KERNEL;

 335                                /*

 336                                 * first pass will use the same initial

 337                                 * identity mapping attribute.

 338                                 */

 339                                pgprot_t init_prot = __pgprot(PTE_IDENT_ATTR);

 340

 341                                if (is_kernel_text(addr))

 342                                        prot = PAGE_KERNEL_EXEC;

 343

 344                                pages_4k++;

 345                                if (mapping_iter == 1) {

 346                                        set_pte(pte, pfn_pte(pfn, init_prot));

 347                                        last_map_addr = (pfn << PAGE_SHIFT) + PAGE_SIZE;

 348                                } else

 349                                        set_pte(pte, pfn_pte(pfn, prot));

 350                        }

 351                }

 352        }

 353        if (mapping_iter == 1) {

 354                /*

 355                 * update direct mapping page count only in the first

 356                 * iteration.

 357                 */

 358                update_page_count(PG_LEVEL_2M, pages_2m);

 359                update_page_count(PG_LEVEL_4K, pages_4k);

 360

 361                /*

 362                 * local global flush tlb, which will flush the previous

 363                 * mappings present in both small and large page TLB's.

 364                 */

 365                __flush_tlb_all();

 366

 367                /*

 368                 * Second iteration will set the actual desired PTE attributes.

 369                 */

 370                mapping_iter = 2;

 371                goto repeat;

 372        }

 373        return last_map_addr;

 374}

 

通过作者的注释, 可以了解到这个函数的作用是把整个物理内存地址都映射到从内核空间的开始地址,即从0xc0000000的整个内核空间中,直到物理内存映射完毕为止。这个函数比较长, 而且用到很多关于内存管理方面的宏定义,理解了这个函数, 就能大概理解内核是如何建立页表的,将这个抽象的模型完全的理解。

 

函数开始定义了4个变量pgd_t *pgd pmd_t *pmd pte_t *pte pfnpgd指向一个目录项开始的地址,pmd指向一个中间目录开始的地址,pte指向一个页表开始的地址pfn是页框号被初始为0。注意这个页全局目录地址swapper_pg_dir,在文件arch/x86/kernel/head_32.S第一次定义后,就再也没变过,前面find_early_table_space函数只是为页表分配新的空间,然后在这里为页表项赋值。看到280行,有个pgd_index宏,确定从页全局目录的哪个位置开始设置内核临时页表:

#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))

我们得到的pgd_idx768,也就是从虚拟地址0xC0000000处开始映射,定位主内核页全局目录的起始项pgd=768,也是内核要从目录表中第768个表项开始进行设置。从7681024这个256个表项被linux内核设置成内核目录项,低768个目录项被用户空间使用。pgd = pgd_base + pgd_idx pgd便指向了第768个表项。

 

然后函数开始一个循环即开始填充从7681024256个目录项的内容。one_md_table_init()函数根据pgd找到指向的pmd表。279pfn = start_pfn,也就是为0,物理地址从0x0000 0000开始,起始页框号(page frame number)pfn,因为我们只分析4k的那个map_range结构,这个结构的start0endmax_low_pfn<<PAGE_SHIFT,也就是所有能使用页面的最后一个页面的地址。那么传递给kernel_physical_mapping_init的就是这样两个参数再加一个mask。对这样一个范围,0~max_low_pfn,函数282~352行代码就设置页表和页目录的值。

 

285行的if (pfn >= end_pfn)很关键,前面讲了max_low_pfn代表着整个物理内存一共有多少页框。当pfn大于max_low_pfn的时候,表明内核已经把整个物理内存都映射到了系统空间中, 所以剩下有没被填充的表项就直接忽略了。因为内核已经可以映射整个物理空间了, 没必要继续填充剩下的表项。

 

紧接293行第2for循环,在linux3级映射模型中,是要设置pmd

抱歉!评论已关闭.