首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 操作系统 > Linux > 内核页表的初始化分析 (转载)
【标  题】:内核页表的初始化分析 (转载)
【关键字】:
【来  源】:http://www.cublog.cn/u/12757/showart.php?id=112195

内核页表的初始化分析 (转载)

-------------------- 在head.s中进行第一次初始化 -----------------------
内核页目录swapper_pg_dir是在head.s中静态分配的,位于物理内存0x101000处,虚拟地址3G+0x101000处。


/*
 * Initialize page tables
 */
	movl $pg0-__PAGE_OFFSET,%edi /* initialize page tables */       // 2个页表pg0和pg1初始化,分别映射到物理内存0~4M和4~8M
	movl $007,%eax		/* "007" doesn't mean with right to kill, but
				   PRESENT+RW+USER */              //设置存在、可读写、用户态的标志
2:	stosl                                                    //注意:edi中存放的都是pg0,pg1页表项的物理地址(因为页机制还没开启)
	add $0x1000,%eax
	cmp $empty_zero_page-__PAGE_OFFSET,%edi               //循环初始化,一次一个页表项,每次eax的值增加4096,作为下一次的赋值
	jne 2b                                                //当设置完pg1的最后一项,循环结束

/*
 * Enable paging
 */
3:
	movl $swapper_pg_dir-__PAGE_OFFSET,%eax
	movl %eax,%cr3		/* set the page table pointer.. */    //将页目录表swapper_pg_dir的物理地址赋给%cr3。
	movl %cr0,%eax                                                
	orl $0x80000000,%eax                                          
	movl %eax,%cr0		/* ..and set paging (PG) bit */       //启用页机制 
	jmp 1f			/* flush the prefetch-queue */
1:
	movl $1f,%eax
	jmp *%eax		/* make sure eip is relocated */      //长跳转将IP指令计数器中的指令指针转为虚拟指针(内核自3G+1M开始的)
1:                                                                    //此前一直是用的物理地址(内核从1M开始)
	/* Set up the stack pointer */
	lss stack_start,%esp

。。。。。。。。。。。。

.org 0x1000
ENTRY(swapper_pg_dir)                     //页目录表,有4项已预先赋好值了。在paging_init()中将重新初始化
	.long 0x00102007                  //页目录表中第1、2和第769、770项都指向pg0和pg1,使得虚拟地址0~8M和3G~3g+8M的空间都映射到了物理内存0~8M。
	.long 0x00103007
	.fill BOOT_USER_PGD_PTRS-2,4,0
	/* default: 766 entries */
	.long 0x00102007
	.long 0x00103007
	/* default: 254 entries */
	.fill BOOT_KERNEL_PGD_PTRS-2,4,0

/*
 * The page tables are initialized to only 8MB here - the final page
 * tables are set up later depending on memory size.
 */
.org 0x2000               //页表0
ENTRY(pg0)

.org 0x3000               //页表1
ENTRY(pg1)

/*
 * empty_zero_page must immediately follow the page tables ! (The
 * initialization loop counts until empty_zero_page)
 */

.org 0x4000
ENTRY(empty_zero_page)





-------------------- 在start_kernel()->paging_init()->pagetable_init()中进行第二次初始化 ---------
该函数实现将从3G到end的虚拟地址空间都建立到物理地址的一对一映射。max_low_pfn是物理内存的最大页数(初始化阶段检测得到)
将它乘以页尺寸再转化为虚拟地址就得到end。页目录表起始地址为swapper_pg_dir(虚拟地址),是在head.s中静态分配的。

static void __init pagetable_init (void)
{
	unsigned long vaddr, end;
	pgd_t *pgd, *pgd_base;
	int i, j, k;
	pmd_t *pmd;
	pte_t *pte;

	/*
	 * This can be zero as well - no problem, in that case we exit
	 * the loops anyway due to the PTRS_PER_* conditions.
	 */
	end = (unsigned long)__va(max_low_pfn*PAGE_SIZE);    //初始化建立虚拟地址空间中从3G到end的映射(max_low_pfn是物理内存的页数)

	pgd_base = swapper_pg_dir;
#if CONFIG_X86_PAE
	for (i = 0; i < PTRS_PER_PGD; i++) {
		pgd = pgd_base + i;
		__pgd_clear(pgd);
	}
#endif
	i = __pgd_offset(PAGE_OFFSET);
	pgd = pgd_base + i;                            //初始化的起始页目录项指针

	for (; i < PTRS_PER_PGD; pgd++, i++) {   //从3G开始,每次循环解决一个页目录项的设置(包括下面的中间目录表的初始化)
		vaddr = i*PGDIR_SIZE;
		if (end && (vaddr >= end))
			break;
#if CONFIG_X86_PAE
		pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);   //若使用三级映射,建立中间目录表
		set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
#else
		pmd = (pmd_t *)pgd;              //使用二级映射机制(页目录、页表),则中间目录项指针就直接指向页目录项
#endif
		if (pmd != pmd_offset(pgd, 0))
			BUG();
		for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {  //每次循环解决一个中间目录项的设置,包括下面的页表的初始化
			vaddr = i*PGDIR_SIZE + j*PMD_SIZE;   //对于二级映射来说,PTRS_PER_PMD=1,pmd = (pmd_t *)pgd
			if (end && (vaddr >= end))           //所以只循环一次,且设置的就是页目录项
				break;                       //若是三级映射,则循环 PTRS_PER_PMD次,Intel CPU的中间目录项数为512
			if (cpu_has_pse) {             //若CPU支持4M的页,则无需页表了,直接初始化中间目录项就可以
				unsigned long __pe;    //对二级映射来说,即页目录项

				set_in_cr4(X86_CR4_PSE);
				boot_cpu_data.wp_works_ok = 1;
				__pe = _KERNPG_TABLE + _PAGE_PSE + __pa(vaddr);
				/* Make it "global" too if supported */
				if (cpu_has_pge) {
					set_in_cr4(X86_CR4_PGE);
					__pe += _PAGE_GLOBAL;
				}
				set_pmd(pmd, __pmd(__pe));  //对二级映射来说,是设置页目录项的值
				continue;                //直接进行下一轮循环,无需建立页表
			}

			pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);  //CPU不支持4M页,还得建立页表(分配并置值)
			set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));

			if (pte != pte_offset(pmd, 0))
				BUG();

			for (k = 0; k < PTRS_PER_PTE; pte++, k++) {          //设置各页表项的值(循环1024次)
				vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
				if (end && (vaddr >= end))
					break;
				*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
			}
		}
	}

	/*
	 * 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;
	fixrange_init(vaddr, 0, pgd_base);

#if CONFIG_HIGHMEM                                           
	/*
	 * Permanent kmaps:
	 */
	vaddr = PKMAP_BASE;
	fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);

	pgd = swapper_pg_dir + __pgd_offset(vaddr);
	pmd = pmd_offset(pgd, vaddr);
	pte = pte_offset(pmd, vaddr);
	pkmap_page_table = pte;
#endif

#if CONFIG_X86_PAE
	/*
	 * Add low memory identity-mappings - SMP needs it when
	 * starting up on an AP from real-mode. In the non-PAE
	 * case we already have these mappings through head.S.
	 * All user-space mappings are explicitly cleared after
	 * SMP startup.
	 */
	pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];
#endif
}
拉环的故事:【上一篇】
IBM266服务器上安装Linux EL3+Oralce9i+Jboss安装:【下一篇】
【相关文章】
没有相关文章
【随机文章】
  • 制造企业实施CRM如何战略定位?
  • 浅析本机API (zt)
  • 中国开源众生相-也谈“中国人的开源”
  • as4+postfix+cyrus-sasl+mysql+postfixadmin+courier-
  • Asp.net中Frameset的使用小结
  • 顺利打开Outlook Express奇怪的邮件附件
  • A Complete Screen Saver Framework Using MFC
  • BSTR类型解析
  • oracle for linux full !!!
  • 多客户环境下VB 数据库编程之(四)
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 软讯网络 All Rigths Reserved.