首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

历程页表与内核页表:页表的初始化

2013-10-27 
进程页表与内核页表:页表的初始化摘要:linux刚刚加电启动时,如何从实模式进入保护模式?启动分页机制的前提

进程页表与内核页表:页表的初始化

摘要:linux刚刚加电启动时,如何从实模式进入保护模式?启动分页机制的前提是什么?如何保证分页机制之前和之后通过实地址和虚拟地址都能访问到同一个物理地址呢?内核页表是如何进行初始化的?用户进程不能访问内核的数据是在初始化的哪个阶段决定的?这些内容,都牵扯到linu的进程页表和内核页表,以及内核页表的初始化。本文也主要为你解答这些疑问.


本文来源:进程页表和内核页表:页表的初始化


1.进程页表:

关键数据结构:

PAGE_OFFSET: 0xc000000


进程地址空间以0xc0000000(由宏PAGE_OFFSET定义)分割成两个部分,进程运行在用户态,产生的地址小于0xc0000000;进程运行在核心态,产生的地址大于0xc0000000.但是,在某些情况下,内核为了访问数据必须访问用户态线性地址空间。


进程地址空间,其中的内核态部分对于所有的进程都是一样的,等于主内核页全局目录的相应表项。


2.内核页表


内核维护着自己的页表,驻留在所谓的主内核页全局目录中。我们将在此处解释:内核如何初始化自己的页表。

1)第一阶段:内核镜像刚刚装入内存,CPU处于实模式,分页功能尚未开启,内核创建一个有限的地址空间,128K,仅仅将内核装入RAM并初始化核心数据。

2)第二阶段:内核充分利用剩余的RAM建立页表,下面,我们将详细讨论这个页表的建立过程。


2.1临时内核页表



关键数据结构:

swapper_pg_dir:临时页全局目录对应的虚拟地址。

pg0:第一个页所在的物理地址


临时页全局目录在编译内核过程中静态初始化,而临时页全局目录存放在swapper_pg_dir之中。临时页表从pg0变量处开始存放。这里,我们假设内核使用的段,临时页表和128KB的内存初始化数据可以存放在前8M的RAM之中。为了映射这8M,我们需要用到2个页表。


开启分页的首要任务是确保实模式和保护模式下都能对前8M进行寻址(参考其中有关控制寄存器CR3的部分)。就是说,从0x0000000到0x007fffff的线性地址,从0xc0000000到0xc07fffff均可映射到物理地址范围:0x00000000到0x007fffff。

是startup_32()来初始化的。它的等价代码(这段代码见于2.4.0内核,2.6内核以后不是这样)如下:


314 static void __init pagetable_init (void)315 {316         unsigned long vaddr, end;317         pgd_t *pgd, *pgd_base;318         int i, j, k;319         pmd_t *pmd;320         pte_t *pte;321 322         /*323          * This can be zero as well - no problem, in that case we exit324          * the loops anyway due to the PTRS_PER_* conditions.325          */326         end = (unsigned long)__va(max_low_pfn*PAGE_SIZE);327 328         pgd_base = swapper_pg_dir;329 #if CONFIG_X86_PAE330         for (i = 0; i < PTRS_PER_PGD; i++) {331                 pgd = pgd_base + i;332                 __pgd_clear(pgd);333         }334 #endif335         i = __pgd_offset(PAGE_OFFSET);336         pgd = pgd_base + i;337 338         for (; i < PTRS_PER_PGD; pgd++, i++) {339                 vaddr = i*PGDIR_SIZE;340                 if (end && (vaddr >= end))341                         break;342 #if CONFIG_X86_PAE343                 pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);344                 set_pgd(pgd, __pgd(__pa(pmd) + 0x1));345 #else346                 pmd = (pmd_t *)pgd;347 #endif348                 if (pmd != pmd_offset(pgd, 0))349                         BUG();350                 for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {351                         vaddr = i*PGDIR_SIZE + j*PMD_SIZE;352                         if (end && (vaddr >= end))353                                 break;354                         if (cpu_has_pse) {355                                 unsigned long __pe;356 357                                 set_in_cr4(X86_CR4_PSE);358                                 boot_cpu_data.wp_works_ok = 1;359                                 __pe = _KERNPG_TABLE + _PAGE_PSE + __pa(vaddr);360                                 /* Make it "global" too if supported */361                                 if (cpu_has_pge) {362                                         set_in_cr4(X86_CR4_PGE);363                                         __pe += _PAGE_GLOBAL;364                                 }365                                 set_pmd(pmd, __pmd(__pe));366                                 continue;367                         }368 369                         pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);370                         set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));371 372                         if (pte != pte_offset(pmd, 0))373                                 BUG();374 375                         for (k = 0; k < PTRS_PER_PTE; pte++, k++) {376                                 vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;377                                 if (end && (vaddr >= end))378                                         break;379                                 *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);380                         }381                 }382         }383 384         /*385          * Fixed mappings, only the page table structure has to be386          * created - mappings will be set by set_fixmap():387          */388         vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;389         fixrange_init(vaddr, 0, pgd_base);390 391 #if CONFIG_HIGHMEM392         /*393          * Permanent kmaps:394          */395         vaddr = PKMAP_BASE;396         fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);397 398         pgd = swapper_pg_dir + __pgd_offset(vaddr);399         pmd = pmd_offset(pgd, vaddr);400         pte = pte_offset(pmd, vaddr);401         pkmap_page_table = pte;402 #endif403 404 #if CONFIG_X86_PAE405         /*406          * Add low memory identity-mappings - SMP needs it when407          * starting up on an AP from real-mode. In the non-PAE408          * case we already have these mappings through head.S.409          * All user-space mappings are explicitly cleared after410          * SMP startup.411          */412         pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];413 #endif414 }

由startup_32()函数创建的物理内存前8M的恒等转化在这种映射不再必要的时候,需要调用zap_low_mappings()进行撤销。


2.3当RAM在896M和4096M之间的最终内核页表


这种情况下,并不把RAM全部映射到内核地址空间。linux在初始化阶段将把一个具有896MB的窗口映射到内核线性地址空间。如果一个程序需要对896M以上的地址进行寻址,那么就必须把线性地址映射到对应的RAM,这意味这修改某些页表项的值。内核使用与前一种情况相同的代码来初始化页全局目录。


2.4当RAM大于4096MB时候的最终内核页表


此时,线性地址只有1G和RAM大于1G,此处的映射就可能涉及到PAE和高端内存,详细可以参考高端内存。此时,linux仅仅映射前896M的RAM,剩下的不进行映射(剩下的就用于存放用户数据了)。与前两种的主要差异在于,此时,采用三级分页模型,代码如下:


。。。。。。

页全局目录的前三项与用户线性地址空间对应,内核使用一个空页(empty_zero_page)进行初始化,第四项,采用页中间目录的地址进行初始化,改页中间目录是通过调用alloc_bootmem_low_pages()获得的。页中间目录的前448项用RAM的物理地址填充。

然后页全局目录的第四项被拷贝到第一项中,这样好为线性地址空间的前896M中的第物理内存映射做镜像。为了完成对SMP系统的初始化,这个映射是必须的,初始化完成以后,内核调用zap-low_mappings()清楚对应的页表项。

热点排行