linux内核学习笔记【二】最终内核页表 Final kernel Page Table
2011-11-10 16:02
417 查看
之前建立了临时页表,现在要建立最终内核页表,内核必须首先要建立一个完整的页表才能继续运行,因为内存寻址是内核继续运行的前提。下面就对主要函数kernel_physical_mapping_init(),进行分析。这个函数的掉用关系为:head.S->start_kernel()->setup_arch()->paging_init()->pagetable_init()->kernel_physical_mapping_init.
这样,内核空间的线性地址0xc0000000-0xffffffff已经都映射了整块物理内存。即pgd[0x300]-pgd[0x400]都有了值,同时下面的每个pte[0-1024]也都有了值,不知道理解的对不对,希望高手指点。还有一个疑问就是,用户空间的线性地址0x00000000 - 0xbfffffff这段地址是在什么时候映射的,可能还没有看到吧,继续学习。。。
static void __init kernel_physical_mapping_init(pgd_t *pgd_base) { unsigned long pfn; pgd_t *pgd; pmd_t *pmd; pte_t *pte; int pgd_idx, pmd_idx, pte_ofs; pgd_idx = pgd_index(PAGE_OFFSET); //#define pgd_index(address) (((address) >> PGDIR_SHIFT) & (PTRS_PER_PGD-1)) in inculde/pagetable.h //so pgd_index = (0xc0000000 >> 22) & (1024 - 1) = 0x300 & 0x3ff = 0x300 = 768 pgd = pgd_base + pgd_idx; //pgd_t *pgd_base = swapper_pg_dir; //pgd = swapper_pg_dir[0x300] pfn = 0; //for(pgd_idx = 768; pgd_idx < 1024; pgd++,pgd_indx++) for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) { pmd = one_md_table_init(pgd); //pmd = pgd if (pfn >= max_low_pfn) //max_low_pfn = 1024 * 1024? continue; for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) { //only have 2 level directory, so PTRS_PER_PMD = 1 unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET; //address = 0 * 4096 + 0xc0000000 = 0xc0000000 // 1 * 4096 = 0xc0001000 // ... /* Map with big pages if possible, otherwise create normal page tables. */ if (cpu_has_pse) { unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1; if (is_kernel_text(address) || is_kernel_text(address2)) set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC)); else set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE)); pfn += PTRS_PER_PTE; } else { pte = one_page_table_init(pmd); //then we have mapped one page table for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) { if (is_kernel_text(address)) set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); else set_pte(pte, pfn_pte(pfn, PAGE_KERNEL)); //set every page table entry PAGE_KERNEL_EXEC or PAGE_KERNEL depend onthe address } } } } } static pmd_t * __init one_md_table_init(pgd_t *pgd) { pud_t *pud; pmd_t *pmd_table; #ifdef CONFIG_X86_PAE //... #else pud = pud_offset(pgd, 0); //#define pud_offset(pgd, start) (pgd) ;in include/generic/4level-fixup.h //so pud = pgd; pmd_table = pmd_offset(pud, 0); //#define pmd_offset(pud, address) ((pmd_t *) pud_page(*(pud)) + pmd_index(address)) ; in /include/asm-i386/pgtable-3level.h //so pmd_table = pgd #endif return pmd_table; } /* * Create a page table and place a pointer to it in a middle page * directory entry. */ static pte_t * __init one_page_table_init(pmd_t *pmd) { if (pmd_none(*pmd)) { //check if the pmd so as pgd has value pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); //alloc physical memory, size = 4096 set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); //set parameters if (page_table != pte_offset_kernel(pmd, 0)) BUG(); return page_table; } return pte_offset_kernel(pmd, 0); } static inline int is_kernel_text(unsigned long addr) { if (addr >= (unsigned long)_stext && addr <= (unsigned long)__init_end) return 1; return 0; } //_stext, __init_end是个内核符号, 在内核链接的时候生成的, 分别表示内核代码段的开始和终止地址. //如果address属于内核代码段, 那么在设置页表项的时候就要加个PAGE_KERNEL_EXEC属性,如果不是,则加个PAGE_KERNEL属性. //#define _PAGE_KERNEL_EXEC \ // (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED) //#define _PAGE_KERNEL \ // (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_NX)
这样,内核空间的线性地址0xc0000000-0xffffffff已经都映射了整块物理内存。即pgd[0x300]-pgd[0x400]都有了值,同时下面的每个pte[0-1024]也都有了值,不知道理解的对不对,希望高手指点。还有一个疑问就是,用户空间的线性地址0x00000000 - 0xbfffffff这段地址是在什么时候映射的,可能还没有看到吧,继续学习。。。
相关文章推荐
- linux内核学习笔记【二】最终内核页表 Final kernel Page Table
- linux内核学习笔记【一】临时内核页表 Provisional kernel Page Tables
- linux内核学习笔记【一】临时内核页表 Provisional kernel Page Tables
- linux内核学习笔记【一】临时内核页表 Provisional kernel Page Tables
- 【Linux学习笔记②】Linux系统内核Kernel和GNU计划
- (笔记)Linux内核学习(九)之内核内存管理方式
- 查看内核页表kernel_page_tables (aarch32)
- Linux内核学习笔记九——内核内存管理方式
- windbg 学习笔记 FOR 内核调试(三) --进程句柄表HANDLE_TABLE
- Linux内核学习笔记:内核同步
- linux内核学习笔记-Linux 内核系统体系结构
- linux内核分析学习笔记:用gdb跟踪linux内核启动过程
- linux学习笔记-读《Linux设备驱动开发详解》~第三章 Linux内核及内核编程
- 【学习笔记】编译Linux内核(上)--编译基于x86平台的Linux内核的过程
- 【学习笔记】编译Linux内核(中)--安装内核时易出现的问题和解决方案
- Linux内核分析第六周学习笔记——分析Linux内核创建一个新进程的过程
- Linux内核学习笔记九——内核内存管理方式
- Linux内核设计与实现 学习笔记(8)内核调试
- Linux内核学习笔记——内核内存管理方式
- Linux内核学习笔记九——内核内存管理方式