MIT JOS LAB4学习笔记

Lab4

Part A: 多处理器支持和协作式多任务

练习 1 :实现在 kern/pmap.c 中的 mmio_map_region 方法。你可以看看 kern/lapic.c 中的 lapic_init 开头部分,了解一下它是如何被调用的。你还需要完成接下来的练习,你的 mmio_map_region 才能够正常运行。

lapic_init()函数的一开始就调用了该函数,将从lapicaddr 开始的4kB物理地址映射到虚拟地址,并返回其起始地址。注意到,它是以页为单位对齐的,每次都映射一个页的大小。

练习 2:阅读 kern/init.c 中的 boot_aps() 和 mp_main() 方法,和 kern/mpentry.S 中的汇编代码。确保你已经明白了引导 AP 启动的控制流执行过程。接着,修改你在 kern/pmap.c 中实现过的 page_init() 以避免将 MPENTRY_PADDR 加入到 free list 中,以使得我们可以安全地将 AP 的引导代码拷贝于这个物理地址并运行。你的代码应当通过我们更新过的 check_page_free_list() 测试,不过可能仍会在我们更新过的 check_kern_pgdir() 测试中失败,我们接下来将解决这个问题。

修改 kern/pmap.c中的page_init()将MPENTRY_PADDR(0x7000)这一页不要加入到page_free_list。

问题 1:逐行比较 kern/mpentry.S 和 boot/boot.S。牢记 kern/mpentry.S 和其他内核代码一样也是被编译和链接在 KERNBASE 之上运行的。那么,MPBOOTPHYS 这个宏定义的目的是什么呢?为什么它在 kern/mpentry.S 中是必要的,但在 boot/boot.S 却不用?换句话说,如果我们忽略掉 kern/mpentry.S 哪里会出现问题呢?
提示:回忆一下我们在 Lab 1 讨论的链接地址和装载地址的不同之处。

1.kern/mpentry.S 与 boot/boot.S 有以下差别:

没有 Enable A20 的部分

GDT 相关的地址都用 MPBOOTPHYS 宏包装了一下

栈设置在了 mpentry_kstack

跳转到入口 mp_main

2.MPBOOTPHYS 宏的作用:

MPBOOTPHYS 的作用是将高地址变为地址。

因为 kern/mpentry.S 都链接到了高位的虚拟地址,但是实际上装载在低位的物理地址,所以 MPBOOTPHYS 要把这个高位的地址映射到低位的地址。boot/boot.S 装载在低位并且链接也在低位,所以就不需要这样的宏。

练习 3:修改位于 kern/pmap.c 中的 mem_init_mp(),将每个CPU堆栈映射在 KSTACKTOP 开始的区域,就像 inc/memlayout.h 中描述的那样。每个堆栈的大小都是 KSTKSIZE 字节,加上 KSTKGAP 字节没有被映射的 守护页 。现在,你的代码应当能够通过我们新的 check_kern_pgdir() 测试了。

对于 CPU i, 、物理地址为 'percpu_kstacks[i]',然后映射过去就行。

练习 4 :位于kern/trap.c 中的 trap_init_percpu() 为 BSP 初始化了 TSS 和 TSS描述符,它在 Lab 3 中可以工作,但是在其他 CPU 上运行时,它是不正确的。修改这段代码使得它能够在所有 CPU 上正确执行。(注意:你的代码不应该再使用全局变量 ts。)

每一个 cpu 的 task state segment (TSS)被用来指定每一个 CPU 的内核栈存在的地方,The TSS for CPU i 储存在 cpus[i].cpu_ts,相应的 TSS descriptor 定义在 gdt[(GD_TSS0 >> 3) + i]。先利用cpu_id建立TSS,初始化TSS descriptor,之后加载TSS selector,最后加载IDT(中断描述符表)。

加锁

你应当在以下 4 个位置使用全局内核锁:

  • i386_init() 中,在 BSP 唤醒其他 CPU 之前获得内核锁
  • mp_main() 中,在初始化完 AP 后获得内核锁,接着调用 sched_yield() 来开始在这个 AP 上运行进程。
  • trap() 中,从用户模式陷入(trap into)内核模式之前获得锁。你可以通过检查 tf_cs 的低位判断这一 trap 发生在用户模式还是内核模式(译者注:Lab 3 中曾经使用过这一检查)
  • env_run() 中,恰好在回到用户进程之前释放内核锁。不要太早或太晚做这件事,否则可能会出现竞争或死锁。

练习 5:在上述提到的位置使用内核锁,加锁时使用 lock_kernel()释放锁时使用 unlock_kernel()

Lock_kernel()的函数定义如下:

Unlock_kernel()的函数定义如下:

在kern/spinlock.cpp中,

spin_lock()函数的定义如下:

其中,while循环体现了循环等待的思想,

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值