linux 内核 内存管理 初始化 页表
2011-11-09 21:17
736 查看
linux内核在启动分页机制时就已经有了一个可用的页表,这个最初的页表是手工创建了,而且仅有为数不多的几个页面,进入start_kernel()以后需要把原来的页表完善一下,具体工作在start_kernel() --> setup_arch() --> paging_init()函数中。paing_init()函数定义在arch/x86/mm/init_32.c中。
void __init paging_init(void)
{
#ifdef CONFIG_X86_PAE
set_nx();
if (nx_enabled)
printk("NX (Execute Disable) protection: active\n");
#endif
pagetable_init();
load_cr3(swapper_pg_dir);
#ifdef CONFIG_X86_PAE
if (cpu_has_pae)
set_in_cr4(X86_CR4_PAE);
#endif
__flush_tlb_all();
kmap_init();
}
页表初始化的工作由pagetable_init()函数完成,load_cr3()的功能是装载页表。
static void __init pagetable_init (void)
{
unsigned long vaddr, end;
pgd_t *pgd_base = swapper_pg_dir;
paravirt_pagetable_setup_start(pgd_base); //?什么用?
/* Enable PSE if available */
if (cpu_has_pse)
set_in_cr4(X86_CR4_PSE);
/* Enable PGE if available */
if (cpu_has_pge) {
set_in_cr4(X86_CR4_PGE);
__PAGE_KERNEL |= _PAGE_GLOBAL;
__PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;
}
/* 把0~896M的物理页框映射到3G~3G+896M,在这个过程中会通过
* bootmem_allocator申请新的内存页,用以存放页表。 */
kernel_physical_mapping_init(pgd_base);
remap_numa_kva();
// null function
/* Fixed mappings, only the page table structure has to be
* created - mappings will be set by set_fixmap() */
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
end = (FIXADDR_TOP + PMD_SIZE - 1) & PMD_MASK;
page_table_range_init(vaddr, end, pgd_base);
/* 把固定映射内存区映射到页目录中 */
permanent_kmaps_init(pgd_base);
paravirt_pagetable_setup_done(pgd_base);
}
kernel_physical_mapping_init()函数把0~896M的物理地址映射到3G以上的虚拟空间。在这个过程中回向bootmem allocator申请需要的内存页(参考bootmem的初始化)。该函数定义在:arch/x86/mm/init_32.c,定义内容如下。
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); // get the index of pgd
pgd = pgd_base + pgd_idx; // get the pgd entry
pfn = 0;
for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
/* pmd is just pgd in x86 architecture's default config*/
pmd = one_md_table_init(pgd);
if (pfn >= max_low_pfn)//这个判断可以保证不建立高端内存对应的页表
continue; /* why not break ? 当where pfn >= max_low_pfn时,
* 虽然不用建立相应的页表,但是pmd这一层次的目录是需要建立的,
* 所以使用continue而不是break */
/* 下面这个for语句循环的次数取决于 PTRS_PER_PMD宏.
* PTRS_PER_PMD的定义在include/asm-generic/pgtable-nopmd.h中,被定义为1*/
for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;
/* 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 {
/* 如果没有与该pmd entry相对应的page table,
* 则为该pmd entry申请一个新的页面作为page table,
* one_page_table_init()的返回值即是page table的首地址 */
pte = one_page_table_init(pmd);
/* 初始化每个pte的值*/
for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn;
pte++, pfn++, pte_ofs++, address += PAGE_SIZE) {
if (is_kernel_text(address))
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
else
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
}
}
}
}
}
函数中使用pmd作为过渡,是为了能够支持3级页表,而linux的x86默认配置是2级页表,所以pmd的存在代码的兼容性。linux在2.6.24中的页表是4级的,从顶到底分别是pgd, pud, pmd和pte,内核在默认配置下屏蔽了pud和pmd。pud的全称是page upper directory, pmd的全程是page middle directory。
void __init paging_init(void)
{
#ifdef CONFIG_X86_PAE
set_nx();
if (nx_enabled)
printk("NX (Execute Disable) protection: active\n");
#endif
pagetable_init();
load_cr3(swapper_pg_dir);
#ifdef CONFIG_X86_PAE
if (cpu_has_pae)
set_in_cr4(X86_CR4_PAE);
#endif
__flush_tlb_all();
kmap_init();
}
页表初始化的工作由pagetable_init()函数完成,load_cr3()的功能是装载页表。
static void __init pagetable_init (void)
{
unsigned long vaddr, end;
pgd_t *pgd_base = swapper_pg_dir;
paravirt_pagetable_setup_start(pgd_base); //?什么用?
/* Enable PSE if available */
if (cpu_has_pse)
set_in_cr4(X86_CR4_PSE);
/* Enable PGE if available */
if (cpu_has_pge) {
set_in_cr4(X86_CR4_PGE);
__PAGE_KERNEL |= _PAGE_GLOBAL;
__PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;
}
/* 把0~896M的物理页框映射到3G~3G+896M,在这个过程中会通过
* bootmem_allocator申请新的内存页,用以存放页表。 */
kernel_physical_mapping_init(pgd_base);
remap_numa_kva();
// null function
/* Fixed mappings, only the page table structure has to be
* created - mappings will be set by set_fixmap() */
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
end = (FIXADDR_TOP + PMD_SIZE - 1) & PMD_MASK;
page_table_range_init(vaddr, end, pgd_base);
/* 把固定映射内存区映射到页目录中 */
permanent_kmaps_init(pgd_base);
paravirt_pagetable_setup_done(pgd_base);
}
kernel_physical_mapping_init()函数把0~896M的物理地址映射到3G以上的虚拟空间。在这个过程中回向bootmem allocator申请需要的内存页(参考bootmem的初始化)。该函数定义在:arch/x86/mm/init_32.c,定义内容如下。
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); // get the index of pgd
pgd = pgd_base + pgd_idx; // get the pgd entry
pfn = 0;
for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {
/* pmd is just pgd in x86 architecture's default config*/
pmd = one_md_table_init(pgd);
if (pfn >= max_low_pfn)//这个判断可以保证不建立高端内存对应的页表
continue; /* why not break ? 当where pfn >= max_low_pfn时,
* 虽然不用建立相应的页表,但是pmd这一层次的目录是需要建立的,
* 所以使用continue而不是break */
/* 下面这个for语句循环的次数取决于 PTRS_PER_PMD宏.
* PTRS_PER_PMD的定义在include/asm-generic/pgtable-nopmd.h中,被定义为1*/
for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) {
unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET;
/* 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 {
/* 如果没有与该pmd entry相对应的page table,
* 则为该pmd entry申请一个新的页面作为page table,
* one_page_table_init()的返回值即是page table的首地址 */
pte = one_page_table_init(pmd);
/* 初始化每个pte的值*/
for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn;
pte++, pfn++, pte_ofs++, address += PAGE_SIZE) {
if (is_kernel_text(address))
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
else
set_pte(pte, pfn_pte(pfn, PAGE_KERNEL));
}
}
}
}
}
函数中使用pmd作为过渡,是为了能够支持3级页表,而linux的x86默认配置是2级页表,所以pmd的存在代码的兼容性。linux在2.6.24中的页表是4级的,从顶到底分别是pgd, pud, pmd和pte,内核在默认配置下屏蔽了pud和pmd。pud的全称是page upper directory, pmd的全程是page middle directory。
相关文章推荐
- Linux初始化内核临时页表之后的setup_memory函数
- linux内存管理解析----linux物理,线性内存布局及页表的初始化
- Linux 内核页表初始化
- linux-3.2.36内核启动3-setup_arch中的内存初始化2(arm平台 分析建立页表)
- linux 内核 内存管理 bootmem alloctor 的初始化
- 区域函数[置顶] linux 3.4.10 内核内存管理源代码分析5:伙伴系统初始化
- linux-3.2.36内核启动3-setup_arch中的内存初始化2(arm平台 分析建立页表)
- linux内核内存管理学习之一(基本概念,分页及初始化)
- arm-linux内存管理学习笔记(2)-内核临时页表的建立
- Linux0.11内核--内存管理之1.初始化
- linux 3.4.10 内核内存管理源代码分析5:伙伴系统初始化
- Linux内核内存管理之BUDDY页面管理(二)
- Linux 内核设备驱动之GPIO驱动之GPIO 框架初始化
- Linux 2.6.36 x86 内核中断初始化过程详解
- linux内核内存管理学习笔记
- 关于进程页表内核部分和内核主页表的关系(Linux 2.6.11)
- linux内存管理--用户空间和内核空间
- arm-linux内核start_kernel之前启动分析(2)- 页表的准备
- linux进程创建与守护;exec 进程内核页表建立
- Linux内存管理 —— 内核态和用户态的内存分配方式