操作系统lab2

OS_lab2实验报告

思考题

Thingking 1

请根据上述说明,回答问题:在编写的 C 程序中,指针变量中存储的地址被视为虚拟地址,还是物理地址?MIPS 汇编程序中 lw和sw 指令使用的地址被视为虚拟地址,还是物理地址?

  • 在C语言程序中指针变量储存的地址为虚拟地址,MIPS汇编程序中lw和sw指令使用的地址为虚拟地址

Thingking 2

请思考下述两个问题:
1. 从可重用性的角度,阐述用宏来实现链表的好处。
2. 查看实验环境中的/usr/include/sys/queue.h,了解其中单向链表与循环链表的实现,比较它们与本实验中使用的双向链表,分析三者在插入与删除操作上的性能差异。

  • 将多条语句封装为一个宏,需要执行相应操作时直接调用宏即可,避免较长的重复代码,同时避免了函数调用过程中的压栈等操作,提高了性能也节省了空间。

  • 在删除上,实验环境中的链表和本实验中的链表,除了访问节点的下一节点时本实验使用宏,其余部分表大致相同,性能上无太大差异,但是实验环境中的循环链表如果删除的节点不与头结点链接,则需要遍历循环链表找到待删除的节点,性能较差。

  • 在插入上,实验环境中的链表和本实验中的链表差异与在删除中一样不大,观察到实验环境中的循环链表插入宏的传入参数为待插入的位置的前一个链表节点,因此性能与本实验中的性能差异不大。

Thingking 3

请阅读include/queue.h以及include/pmap.h,将Page_list的结构梳理清楚

  • 从LIST_EMPTY宏中可知,Page_list中的lh_first为一个结构体指针,由插入宏可知,访问pp_link中的元素不使用->而是用.可知,pp_link不是指针,因此Page_list结构体的结构如下所示:
1
2
3
4
5
6
7
8
9
structPage_list{
struct {
struct{
structPage *le_next;
structPage **le_prev;
}pp_link;
u_shortpp_ref;
}*lh_first;
}

Thingking 4

请思考下面两个问题:
1. 请阅读上面有关TLB的描述,从虚拟内存和多进程操作系统的实现角度,阐述ASID的必要性。 2. 请阅读 MIPS 4Kc 文档《MIPS32® 4K™ Processor Core Family Software User’s Manual》的 Section 3.3.1 与 Section 3.4,结合 ASID 段的位数,说明 4Kc 中可容纳不同的地址空间的最大数量。

  • 在多进程操作系统中,每个进程之间的非共享数据为独立的,但是CPU并不知道自己在执行哪一个进程的程序,其访问不同进程的内存空间使用虚拟地址可能相同,此时需要ASID将相同的虚拟地址映射到不同的物理地址。

  • 查阅可知可容纳不同地址空间的最大数量为8,原文如下:

    1
    The virtual address is extended with the contents of the 8-bit ASID field to form a unique virtual address before translation. 

Thingking 5

请回答下述三个问题:
1. tlb_invalidate和tlb_out的调用关系? 2. 请用一句话概括tlb_invalidate的作用。 3. 逐行解释tlb_out中的汇编代码。

  • tlb_invalidate调用tlb_out

  • tlb_invalidate将TLB中的特定项清空

  • tlb_out中的汇编代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    LEAF(tlb_out)
    .set noreorder
    mfc0 t0, CP0_ENTRYHI # 维护当前表项的虚拟页号和ASID
    mtc0 a0, CP0_ENTRYHI # 将需要清楚的TLB表项的虚拟页号和ASID从函数传参中读入
    nop
    tlbp # 查找EntryHi寄存器中值对应的TLB表项索引并存入Index
    nop
    mfc0 t1, CP0_INDEX # 读出Index
    .set reorder
    bltz t1, NO_SUCH_ENTRY # 判断Index是否有效
    .set noreorder
    mtc0 zero, CP0_ENTRYHI # 将需要清空的TLB表项的各个值清零
    mtc0 zero, CP0_ENTRYLO0
    mtc0 zero, CP0_ENTRYLO1
    nop
    tlbwi # 将清零后的TLB表项的各个值写入TLB中Index指向的位置
    .set reorder

    NO_SUCH_ENTRY:
    mtc0 t0, CP0_ENTRYHI # 将当前维护的虚拟页号和ASID写回
    j ra # 返回
    END(tlb_out)

Thingking 6

请结合 Lab2 开始的 CPU 访存流程与下图中的 Lab2 用户函数部分,尝试将函数调用与CPU访存流程对应起来,思考函数调用与CPU访存流程的关系。

  • CPU访问内存的过程中,如果TLB缺失,陷入内核,调用TLB重填函数,如果在页表中找到对应的数据,则将其填入TLB,否则调用passive_alloc函数,其中调用page_alloc创建一页,继续调用page_insert创建映射关系。

Thingking 7

简单了解并叙述X86体系结构中的内存管理机制,比较X86和MIPS 在内存管理 上的区别。

  • X86:通过分段+分页提供高灵活性和兼容性,硬件复杂度高,适合通用计算场景。

  • MIPS:设计简洁,依赖软件管理TLB,适合嵌入式系统等对实时性要求高的场景。

Thingking A.1

在现代的 64 位系统中,提供了 64 位的字长,但实际上不是 64 位页式存储系统。假设在64位系统中采用三级页表机制,页面大小4KB。由于64位系统中字长为8B,且页目录也占用一页,因此页目录中有512 个页目录项,因此每级页表都需要9位。因此在64位系统下,总共需要3×9+12=39位就可以实现三级页表机制,并不需要64位。
现考虑上述39位的三级页式存储系统,虚拟地址空间为512GB,若三级页表的基地址为PTbase,请计算: 1. 三级页表页目录的基地址。 2. 映射到页目录自身的页目录项(自映射)。 + \((PT_{base}>>9)+PT_{base}\) + \((PT_{base}>>27)+(PT_{base}>>18)+(PT_{base}>>9)+PT_{base}\)

难点分析

C语言中指针变量

C语言指针变量存储的地址为虚拟地址,而页表项中的页地址为物理地址,因此在写入页表项时需要进行虚拟地址到物理地址的转换。CPU访问内存始终使用虚拟地址,因此从页表项读出页地址后需要进行物理地址到虚拟地址的转换。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// do something...
if ((*pgdir_entryp & PTE_V) == 0) {
if (create !=0){
int flag = page_alloc(&pp);
if (flag == -E_NO_MEM){
// do something
} else {
// 写入页表项,需要将页地址转换为物理地址
*pgdir_entryp=page2pa(pp);
// do something...

}
} else {
// do something...
}
}
// 得到当前虚拟地址所对应页表项的地址,这个地址应当是虚拟地址
*ppte = (Pte *)KADDR(PTE_ADDR(*pgdir_entryp))+PTX(va);

在实验中需要时刻清醒地知道当前使用的是虚拟地址还是物理地址,然而在初学实验阶段较难做到。

建立映射关系的问题

在建立映射关系时如果没有找到有效的页,则需要先清除TLB中的对应页,则需要重新建立映射关系,但是这个过程中还需要创建相应的页目录,因此可直接调用pgdir_walk函数,并将create参数设置为1.

实验体会

实验中,学习了分配页的方法(page_alloc),在实践中认识了虚拟地址到物理地址的转换过程,页表页目录的创建过程,虚拟地址到物理地址映射的创建过程,以及CPU访问TLB缺失后执行的中断处理过程,加深了我对CPU访存流程的理解。

原创说明

参考了往年学长的博客https://www.cnblogs.com/emodiary121/p/16142108.html