LAB4_Preemptive Multitasking_PartA Multiprocessor Support and Cooperative Multitasking

简介

激动人心的时刻,我们终于走到了Lab4,OS开始实现进程调度了~

在本实验中,我们将在多个同时活动的用户模式环境中实施抢占式多任务处理。

  • PartA:
    • 为 JOS 增添多处理器支持特性。
    • 实现 round-robin scheduling循环调度。
    • 添加一个基本的环境(进程)管理系统调用(创建和销毁环境,分配和映射内存)。
  • PartB:
    • 实现一个类Unix的fork(),其允许一个用户模式的环境能创建一份它自身的拷贝。
  • PartC:
    • 支持进程间通信(inter-process communication, IPC)
    • 支持硬件时钟中断和抢占

PartA:Multiprocessor Support and Cooperative Multitasking(多处理器支持和协作式多任务处理)

扩展 JOS 使其能运行在多处理器上,实现新的系统调用使用户程序能创建新的进程,实现协作式循环调度。

多处理器支持

我们将让 JOS 支持对称多处理器(symmetric multiprocessing,SMP),这是一种多处理器模型,其中所有CPU都具有对系统资源(如内存和I / O总线)的等效访问。虽然所有CPU在SMP中功能相同,但在引导过程中它们可分为两种类型:

  • 引导处理器(BSP):负责初始化系统和引导操作系统;
  • 应用程序处理器(AP):只有在操作系统启动并运行后,BSP才会激活应用程序处理器。

在SMP系统中,每个CPU都有一个附带的本地APIC(LAPIC)单元。

APIC:Advanced Programmable Interrupt Controller高级可编程中断控制器 。APIC 是装置的扩充组合用来驱动 Interrupt 控制器 [1] 。在目前的建置中,系统的每一个部份都是经由 APIC Bus 连接的。“本机 APIC” 为系统的一部份,负责传递 Interrupt 至指定的处理器;举例来说,当一台机器上有三个处理器则它必须相对的要有三个本机 APIC。自 1994 年的 Pentium P54c 开始Intel 已经将本机 APIC 建置在它们的处理器中。实际建置了 Intel 处理器的电脑就已经包含了 APIC 系统的部份。

LAPIC单元负责在整个系统中提供中断。 LAPIC还为其连接的CPU提供唯一标识符。 在本实验中,我们使用LAPIC单元的以下基本功能(在kern/lapic.c中):

  • 根据LAPIC识别码(APIC ID)区别我们的代码运行在哪个CPU上。(cpunum()
  • 从BSP向APs发送STARTUP处理器间中断(IPI)去唤醒其他的CPU。(lapic_startap()
  • 在Part C,我们编写LAPIC的内置定时器来触发时钟中断,以支持抢占式多任务(pic_init())。

LAPIC的 hole 开始于物理地址0xFE000000(4GB之下的32MB),但是这地址太高我们无法访问通过过去的直接映射(虚拟地址0xF0000000映射0x0,即只有256MB)。但是JOS虚拟地址映射预留了4MB空间在MMIOBASE处,我们需要分配映射空间。

2.1.1. Exercise 1.

Implement mmio_map_region in kern/pmap.c. To see how this is used, look at the beginning of lapic_init in kern/lapic.c. You’ll have to do the next exercise, too, before the tests for mmio_map_region will run.

  // ret -> MMIOBASE 暂存
	void *ret = (void *)base;
	size = ROUNDUP(size, PGSIZE);
	if (base + size > MMIOLIM || base + size < base) {
   
		panic("mmio_map_region reservation overflow\n");
	}
	
	boot_map_region(kern_pgdir, base, size, pa, PTE_W|PTE_PCD|PTE_PWT);
	// base 为static!
	base += size;
	return ret;

2.2. 应用处理器(APs)引导程序

在启动APs之前,BSP应该先收集关于多处理器系统的配置信息,比如CPU总数,CPUs的APIC ID,LAPIC单元的MMIO地址等。 kern/mpconfig.c中的mp_init()函数通过读取驻留在BIOS内存区域中的MP配置表来检索此信息。 也就是说在出厂时,厂家就将此计算机的处理器信息写入了BIOS中,其有一定的规范,也就是kern/mpconfig.cstruct mp定义的。

boot_aps()(在kern / init.c中)函数驱动了AP引导过程。 AP以实模式启动,非常类似于 bootloader 在boot/boot.S中启动的方式,因此boot_aps()将AP进入代码(kern / mpentry.S)复制到可在实模式下寻址的内存位置。与 bootloader 不同,我们可以控制 AP 开始执行代码的位置; 我们将 entry 代码复制到0x7000(MPENTRY_PADDR),但任何未使用的,页面对齐的物理地址低于640KB都可以。

之后,boot_aps函数通过发送STARTUP的IPI(处理器间中断)信号到AP的 LAPIC 单元来一个个地激活AP。在kern/mpentry.S中的入口代码跟boot/boot.S中的代码类似。在一些简短的配置后,它使AP进入开启分页机制的保护模式,调用C语言的setup函数mp_main。boot_aps 等待AP在其结构CpuInfo的cpu_status字段中发出CPU_STARTED标志信号,然后再唤醒下一个。

2.2.1. Exercise 2

  • Read boot_aps() and mp_main() in kern/init.c, and the assembly code in kern/mpentry.S. Make sure you understand the control flow transfer during the bootstrap of APs.
  • Then modify your implementation of page_init() in kern/pmap.c to avoid adding the page at MPENTRY_PADDR to the free list, so that we can safely copy and run AP bootstrap code at that physical address.

整理一下程序运行过程,此过程一直都运行在CPU0,即BSP上,工作在保护模式。

  • i386_init调用了boot_aps(),也就是在BSP中引导其他CPU开始运行
  • boot_aps调用memmove将每个CPU的boot代码加载到固定位置
  • 最后调用lapic_startap执行其bootloader启动对应的CPU

bootloader的地址0x7000在低地址段为第5页,其页数小于npage_basemem: 160,所以将之前代码的前半部分修改为以下代码。

  size_t i;
	for (i = 1; i < MPENTRY_PADDR/PGSIZE; i++) {
   
		pages[i].pp_ref = 0;
	    pages[i].pp_link = page_free_list;
		page_free_list = &pages[i];
	}zheng
	// boot APs entry code
	extern unsigned char mpentry_start[], mpentry_end[];
	size_t size = mpentry_end - mpentry_start;
	size = ROUNDUP(size, PGSIZE);
	for(;i<(MPENTRY_PADDR+size)/PGSIZE; i++) {
   
		pages[i].pp_ref = 1;
	}
	
	for (; i < npages_basemem; i++) {
   
		pages[i].pp_ref = 0;
	    pages[i].pp_link = page_free_list;
		page_free_list = &pages[i];
	}

编译执行后,panic 在check_kern_pgdir

check_page_free_list() succeeded!
check_page_alloc() succeeded!
check_page() succeeded!
kernel panic on CPU 0 at kern/pmap.c:986: assertion failed: check_va2pa(pgdir, base + KSTKGAP + i) == PADDR(percpu_kstacks[n]) + i

2.2.2. Question

  1. 将kern/mpentry.S与boot/boot.S并排比较。 请记住,就像内核中的其他内容一样,kern/mpentry.S被编译、链接并运行在KERNBASE之上,宏MPBOOTPHYS的目的是什么? 为什么这在在kern/mpentry.S
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值