linux内核页表占用内存,浅析linux内核内存管理之最终内核页表

浅析linux内核内存管理之最终内核页表

在系统初始化的时候进行了最终内核映射,主要在paging_init函数中:

499void __init paging_init(void)

500{

501#ifdef CONFIG_X86_PAE

502        set_nx();

503        if (nx_enabled)

504                printk("NX (Execute Disable) protection: active\n");

505#endif

506

507        pagetable_init();

508

509        load_cr3(swapper_pg_dir);

510

511#ifdef CONFIG_X86_PAE

512        /*

513         * We will bail out later - printk doesn't work right now so

514         * the user would just see a hanging kernel.

515         */

516        if (cpu_has_pae)

517                set_in_cr4(X86_CR4_PAE);

518#endif

519        __flush_tlb_all();

520

521        kmap_init();

522        zone_sizes_init();

523}

其中pagetable_init函数主要对直接映射区,永久映射区,固定映射区的页表进行了设置。当设置临时页表的时候,swapper_pg_dir是存放临时页全局目录,范围是0~4G。在建立最终内核页表的时候,仍然使用了swapper_pg_dir这个变量,在pagetable_init函数中对其进行了重新的设置,使其保存3G~4G的页全局目录部分的地址

把swapper_pg_dir的物理地址存放在cr3中

如果CONFIG_X86_PAE,则设置CR4寄存器的第5位,即PAE标志位

使tlb无效

临时内核映射占用的空间是固定内核映射的一部分,从FIX_KMAP_BEGIN~FIX_KMAP_END,由于之前已经设置了固定映射的页中间目录,页表等,所以临时内核映射的页表是存在的。在kmap_init函数中主要就是获得临时内核映射区的第一个页表,即FIX_KMAP_BEGIN对应的页表,将地址存放在kmap_pte变量中

下面分析pagetable_init是怎样建立内核地址空间与直接映射区物理内存的映射的:

310static void __init pagetable_init (void)

311{

312        unsigned long vaddr;

313        pgd_t *pgd_base=swapper_pg_dir;

314

315#ifdef CONFIG_X86_PAE

316        int i;

317        /* Init entries of the first-level page table to the zero page */

318        for (i=0; i

319                set_pgd(pgd_base + i, __pgd(__pa(empty_zero_page) | _PAGE_PRESENT));

320#endif

321

322        /* Enable PSE if available */

323        if (cpu_has_pse) {

324                set_in_cr4(X86_CR4_PSE);

325        }

326

327        /* Enable PGE if available */

328        if (cpu_has_pge) {

329                set_in_cr4(X86_CR4_PGE);

330                __PAGE_KERNEL |= _PAGE_GLOBAL;

331                __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;

332        }

333

334        kernel_physical_mapping_init(pgd_base);

335        remap_numa_kva();

336

337        /*

338         * Fixed mappings, only the page table structure has to be

339         * created - mappings will be set by set_fixmap():

340         */

341        vaddr=__fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;

342        page_table_range_init(vaddr, 0, pgd_base);

343

344        permanent_kmaps_init(pgd_base);

345

346#ifdef CONFIG_X86_PAE

347        /*

348         * Add low memory identity-mappings - SMP needs it when

349         * starting up on an AP from real-mode. In the non-PAE

350         * case we already have these mappings through head.S.

351         * All user-space mappings are explicitly cleared after

352         * SMP startup.

353         */

354        pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];

355#endif

356}

如果是PAE的,那么使用页目录指针表(PDPT),此时pgd只有4项,将其填充为零页的地址

如果设置了PSE,则置位CR4寄存器的第4位

如果设置了PGE,则置位CR4寄存器的第7位

设置直接映射区的页表结构

获得固定映射区起始的线性地址,调用page_table_range_init函数,设置固定映射部分的master kernel page directory,pmd,pte。

调用permanent_kmaps_init函数设置永久映射部分。建立从PKMAP_BASE~PKMAP_BASE+PAGE_SIZE*LASTKMAP的映射,永久映射只使用内核中的一个页表,所以在开启PAE的时候为2MB,否则为4MB,并将页表的地址放入pkmap_page_table变量中

直接映射区的页表建立由kernel_physical_mapping_init函数完成:

143static void __init kernel_physical_mapping_init(pgd_t *pgd_base)

144{

145        unsigned long pfn;

146        pgd_t *pgd;

147        pmd_t *pmd;

148        pte_t *pte;

149        int pgd_idx, pmd_idx, pte_ofs;

150

151        pgd_idx=pgd_index(PAGE_OFFSET);

152        pgd=pgd_base+ pgd_idx;

153        pfn=0;

154

155        for (; pgd_idx 

156                pmd=one_md_table_init(pgd);

157                if (pfn >= max_low_pfn)

158                        continue;

159                for (pmd_idx=0; pmd_idx

160                        unsigned int address=pfn* PAGE_SIZE + PAGE_OFFSET;

161

162                        /* Map with big pages if possible, otherwise create normal page tables. */

163                        if (cpu_has_pse) {

164                                unsigned int address2= (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1;

165

166                                if (is_kernel_text(address) || is_kernel_text(address2))

167                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC));

168                                else

169                                        set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE));

170                                pfn += PTRS_PER_PTE;

171                        } else {

172                                pte=one_page_table_init(pmd);

173

174                                for (pte_ofs=0; pte_ofs

175                                                if (is_kernel_text(address))

176                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));

177                                                else

178                                                        set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));

179                                }

180                        }

181                }

182        }

183}

通过pgd_index计算PAGE_OFFSET在临时页全局目录中的索引值

由于要映射的是直接映射区,所以物理页从物理页0开始

one_md_table_init函数和one_page_table_init函数会调用alloc_bootmem_low_pages分配pmd,pt占用的page,并填充pgd,pmd相应的相应的表项

建立直接映射区物理页与内核线性空间的映射,如果是内核代码段设置为可读,写,执行

注意如果开启了PAE,则设置512项的pmd,否则1项的pmd;当开启PAE,则pte为512项,否则1024项

这样,最终内核页表初始化完成。总结一下:在系统初始化的时候,将直接映射区,永久映射区和固定映射区进行了pgd,pmd,pte的填充。其中直接映射区建立了物理地址到线性地址的映射,而其他的在需要的时候通过kmap,kmap_atomic,set_fixmap进行物理地址到线性地址的映射

再多说一部分,也是系统初始化,页表相关的最后一部分。看一下mem_init函数,除了主要任务bootmem allocator与buddy system的交接之外,还设置了高端内存的页(调用set_highmem_pages_init函数),并调用zap_low_mappings函数清low_memory的映射,内核线程只访问内核空间是不能访问用户空间的,其实low_memory的映射被设置的部分也就是当初为8MB建立的恒等映射填充了临时内核页全局目录的第0项,第1项

376void zap_low_mappings (void)

377{

378        int i;

379

380        save_pg_dir();

381

382        /*

383         * Zap initial low-memory mappings.

384         *

385         * Note that "pgd_clear()" doesn't do it for

386         * us, because pgd_clear() is a no-op on i386.

387         */

388        for (i=0; i

389#ifdef CONFIG_X86_PAE

390                set_pgd(swapper_pg_dir+i, __pgd(1 + __pa(empty_zero_page)));

391#else

392                set_pgd(swapper_pg_dir+i, __pgd(0));

393#endif

394        flush_tlb_all();

395}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值