JOS(2) 分页机制的建立

一、x86分段机制概述

分段机制可以说是从系统启动开始就自动执行的内存管理机制。x86提供了6个16位段寄存器:CS、DS、ES、SS、FS和GS。其中CS是代码段、DS是数据段、SS是堆栈段。在分页机制开启前,通过分段机制获得的线性地址将直接映射/对应到相应的物理内存。实模式下,对应计算物理地址的方式是:
** 物理地址 = 线性地址 = 段寄存器 << 4 + 偏移地址**
这种计算方式对于实模式下还是很便捷的,因为实模式下最高的寻址空间为1MB(0x0010 0000),但在保护模式下,地址空间是32位,无法将32位的地址存入16位的段寄存器,这种情况下,通过使用描述符表来解决分段寻址问题,描述符表中保存了各个段的基地址
描述符表分为全局描述符表和局部描述符表。全局描述符表在全局中唯一,而局部描述符表则可有一个或多个。为了快速方位全局描述符表,x86提供了一个48位的全局描述符表寄存器GDTR,如下图所示。其中32位线性地址用于保存全局描述符表的入口地址,16位寄存器用于保存全局描述符表的长度。当计算器启动后,GDTR的32位线性地址部分将被设置为0,而16位表长度部分将会被设置为0xFFFF。在保护模式初始化过程中必须给GDTR加载新值。

GDTR

由于系统提供了描述符表,所以16位段寄存器只需要保存所对应的段在描述符表中偏移的位置就可以很快的查找到该段的基地址,并在基地址的基础上加上偏移量,就可以获得线性地址。段寄存器中保存的数据格式如下图所示。可以看到,段选择符中只有13位用于记录索引,故全局描述符表最多只有8192项,但由于第0项只能保存为0,所以在全局描述符表中最多保存8191项。


段选择符结构

二、x86分页机制概述

分页机制是x86内存管理机制的第二部分,用于将线性地址转换为物理地址。同时在分页机制下,提供了更多的保护功能。当启动分页机制后,处理器会自动完成上述的地址转换过程,我们需要做的,就是要为处理器分页机制提供页表的设置(在第三部分中会详细的阐述)。这里仅阐述如果通过线性地址找到对应的页表,更详细的分页机制描述可以阅读CSAPP第九章。


页表

这里需要注意的是,通过一级、二级页表可以获得页表的基地址。由于系统对物理内存地址空间进行分页时,要求4K对齐,所以物理内存中的每一页内存的基地址低12位是无用的,故系统利用低12位保存访问权限。

三、JOS内存机制建立过程

这一部分将详细的阐述JOS启动后内存机制建立的过程。主要涉及到的文件有<inc/mmu.h>、<inc/memlayout.h>、<kern/entry.S>、<kern/pmap.c>、<kern/pmap.h>、<kern/entrypgdir.c>
在上一篇文章中,bootloader在初始化的过程我们已经设置好了全局描述符表,而在这里我们主要阐述的是JOS分页内存机制的建立。JOS内核在接管控制后,将开启分页机制,有如下代码。

movl    $(RELOC(entry_pgdir)),  %eax  
movl    %eax, %cr3                        # cr3寄存器用于保存一级页表的基地址
movl    %cr0, %eax
orl  $(CR0_PE|CR0_PG|CR0_WP), %eax     # 开启分页机制
movl    %eax, %cr0

在更进一步的叙述之前,我们需要搞清几个问题。逻辑地址、线性地址和物理地址以及虚拟内存。在分页机制下,虚拟内存机制会一直伴随。对于32位系统,每个程序将会获得4GB的虚拟内存空间,而对于计算机来说,可能并没有如此大的物理内存来装填,所以虚拟内存一般是分配在磁盘上,在需要的时候加载到物理内存中。同时,对虚拟内存空间来说,可以划分为代码段、数据段、堆栈段等等部分,对于每个段的访问,都是通过基地址+偏移量来访问的。基地址是通过段寄存器的值在描述符表中的偏移来获得,而偏移量就是我们所说的逻辑地址;并且,线性地址 = 基地址+偏移地址(逻辑地址);物理地址则是通过线性地址来寻找,通过页表的方式来获取。
JOS的内核地址变换过程如下:JOS内核开始执行后,执行在0x0010 0000处,此后由于开启分页机制,虚拟内存机制也随之一同开启,此后JOS内核将会在虚拟内存空间中的KERNBASE(0xF000 0000)处执行,更重要的是,此后的JOS代码执行将全部使用虚拟内存地址
由于分页机制需要使用页表,在完整的分页机制建立之前,JOS使用了一个“人工手写”的映射关系,将虚拟内存空间中[KERNBASE, KERNBASE+4MB) 、 [0, 4MB)的地址一同映射到物理内存[0, 4MB)处。上述代码中entry_pgdir就指向了手写页表的基地址。
在上述过程完成后,内核将跳转至mem_init()(/kern/pmap.c)处执行,主要的执行代码在/kern/pmap.c文件下,该文件主要完成虚拟地址到物理地址之间的转换,即页表的建立,为此后的系统内存分配提供了保证。

JOS内存空间分布

上图所示的是JOS的虚拟内存空间分布,JOS内核空间位于KERNBASE之上。在/kern/pmap.c中存在以下几个全局变量需要注意:

  • kern_pgdir:内核一级页表指针/基地址
  • pages : 页表信息指针/基地址,将物理内存划分成页后,每一个页i对应一个pages[i]
  • page_free_list : 物理页表空闲链表

3.1 页表初始化

页表初始化在函数page_init()执行,将已经被占用的物理内存页对应的pages做标记,未被占用的物理内存页则加入空闲页链表,该链表可以近似的看做一个栈,当申请空闲物理页时,在该链表的尾部取出一个页信息节点;当释放一个物理页后,将此物理页对应的页信息节点放入链表尾部。该部分的代码如下。

    // 这种实现方式将链表链接起来,近似的看作一个栈(stack),每次的分配从栈顶开始
    /*    +--------------+
     *    +    page n    +    <--- page_free_list
     *    +--------------+
     *            |                     |
     *            |                     |
     *            v                     v
     *    +--------------+         increasing
     *    +    page n-1  +              
     *    +--------------+
     *            |
     *            |
     *            v
     *           ...
     *    +--------------+
     *    +    page 0    +
     *    +--------------+
     *            |
     *            |
     *            v
     *          NULL         // if page_free_list == NULL, [Out of memory]
     */
    page_free_list = NULL;

    for(i = 0; i < npages; i++) {
        if(i == 0) {     
            // Page0 : Marked Used
            pages[i].pp_ref  = 1;
            pages[i].pp_link = NULL;
        }
        else if(i >= 1 && i < npages_basemem) {
            // Base memory : Marked Free
            pages[i].pp_ref  = 0;
            pages[i].pp_link = page_free_list;
            page_free_list   = &pages[i];
        }
        else if(i >= IOPHYSMEM / PGSIZE && i < EXTPHYSMEM / PGSIZE) {
            // IO Hole : Marked Used
            pages[i].pp_ref  = 1;
            pages[i].pp_link = NULL;
        } 
        else if(i >= EXTPHYSMEM / PGSIZE && i < ((int)(boot_alloc(0) - KERNBASE)) / PGSIZE) {
            // 此前已经分配的内存设置为已分配, 此部分属于0-4MB区间内, 属于初始化过程中已经提前建立的分页映射
            pages[i].pp_ref  = 1;
            pages[i].pp_link = NULL;
        }
        else {
            // Extend Memory : Marked Free
            pages[i].pp_ref  = 0;
            pages[i].pp_link = page_free_list;
            page_free_list   = &pages[i];
        }
    }

3.2 内存映射

UVPT、UPAGES分别是kern_pgdir、pages的映射,指向了同一物理内存空间,但内核对UVPT与UPAGES设置了权限,用户(user)只能读取,不可修改。UVPT、UPAGES的位置在JOS内存空间分布的图中指明。下面的代码完成了映射的过程。

// Code 1:
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
// Code 2:
int i, perm = PTE_U | PTE_P, nsize = ROUNDUP(npages * sizeof(struct PageInfo), PGSIZE);
for(int i = 0; i < nsize; i += PGSIZE)
    page_insert(kern_pgdir, pa2page(PADDR(pages) + i), (void*)(UPAGES + i), perm);

下图是UVPT映射过程。可以看到,UVPT对应的二级页表的基地址实际上指向的是kern_pgdir的物理地址,从而UVPT处存放了一级页表的只读副本(内核只读、用户只读)。UPAGES的映射方式与UVPT类似,这里不再赘述。


UVPT映射

至此,JOS的分页内存建立的过程的核心代码就是叙述完成。如果能够绕过这个弯,还是相对容易理解的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,492评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,048评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,927评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,293评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,309评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,024评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,638评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,546评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,073评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,188评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,321评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,998评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,678评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,186评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,303评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,663评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,330评论 2 358

推荐阅读更多精彩内容

  • 1 内存寻址 1.1 物理地址、虚拟地址以及线性地址 物理地址: 物理内存的内存单元地址 虚拟地址: 程序员看到的...
    疯狂小王子阅读 2,818评论 3 21
  • Introduction 该 lab 主要需要编写操作系统的内存管理部分。内存管理分为两个部分: 内核的物理内存分...
    找不到工作阅读 12,221评论 0 12
  • 简介 在 lab4 中我们将实现多个同时运行的用户进程之间的抢占式多任务处理。在 part A 中,我们需要给 J...
    找不到工作阅读 6,840评论 0 7
  • 母亲,如果你是雨,我便是虹,如果你是花,我便是果,如果你是贝,我便是珍珠,母亲,你是我心中的暖阳,你是我生命里最伟...
    英荣摆渡ly阅读 237评论 2 5
  • 月下栏杆枕上眠,柔波淙水对影怜。 案前新墨无人濡,素笺难载故人言。
    一尾涟漪阅读 167评论 0 3