MIT6.828 32位操作系统笔记(15)----系统调度、IPC和页面失效控制 LAB4下

MIT EDU 6.828 实验源代码

分类 MIT6.828 32位操作系统实验笔记  

实验完善代码 LAB2-4下载链接 提取码:79t8

  

1、Part C : 抢先多任务和进程间通信

  LAB 4 的最后一部分将修改代码以实现抢先多任务,并实现进程间的通信。

(1) 时钟中断和抢占

  运行程序 user/spin,这个程序中,父进程fork() 子进程,然后子进程一旦得到CPU 的使用权,将永远自旋,内核和父进程将永远不会再得到控制权。很明显,目前的操作系统代码是有缺陷的,任何用户进程都可能会通过自旋夺取系统的控制权,内核将不会得到控制权。为了让内核抢占一个正在运行的进程,强迫重新获得CPU 的控制权,我们必须扩展JOS 系统使其支持外部的时钟硬件中断。

(2) 时钟中断设置

  外部中断用IRQ 表示,一共有16中外部中断分别用标号0-15 表示。目前,外部中断与中断描述符表之间的映射关系还没有建立。picirq.c 文件中的 pic_init() 函数 将外部中断0-15 映射到 IDT 的 IRQ_OFFSETIRQ_OFFSET + 15.
   在inc/trap.h中,IRQ_OFFSET 被定义为32,因此IRQ 的中断向量为32-47,例如时钟中断的IRQ 编号为0,则它的中断描述符为IDT[IRQ_OFFSET+0] , 这样的设置可以避免外部中部和系统中断之间的重叠。
   在JOS 系统中,我们进行了一个重要的简化,内核态下外部设备中断总是关闭的。外部中断被 %eflags 寄存器的 FL_IF位 控制。当这一位为 1 的时候表示外部中断启用,为0 表示外部中断禁用。由于我们简化为只在用户态启用,那么当系统从用户态切换到内核态的时候,将该位 置 1;反之置0。
   我们需要确保在用户态 FL_IF位 是1,这样当用户态运行的时候可以接受这个中断,并处理这个中断。现在的代码中,在 boot loader 启动的时候就关闭了中断,并且到目前为止都没有再开启中断。

  • Exercise 13
    在这里插入图片描述
/ kern/trapentry.S///
	TRAPHANDLER_NOEC(Trap_IRQ_0, IRQ_OFFSET + 0);//32, 0号外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_1, IRQ_OFFSET + 1);//33, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_2, IRQ_OFFSET + 2);//34, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_3, IRQ_OFFSET + 3);//35, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_4, IRQ_OFFSET + 4);//36, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_5, IRQ_OFFSET + 5);//37, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_6, IRQ_OFFSET + 6);//38, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_7, IRQ_OFFSET + 7);//39, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_8, IRQ_OFFSET + 8);//40, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_9, IRQ_OFFSET + 9);//41, 外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_10, IRQ_OFFSET + 10);//42,外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_11, IRQ_OFFSET + 11);//43,外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_12, IRQ_OFFSET + 12);//44,外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_13, IRQ_OFFSET + 13);//45,外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_14, IRQ_OFFSET + 14);//46,外部中断
	TRAPHANDLER_NOEC(Trap_IRQ_15, IRQ_OFFSET + 15);//47,外部中断

// kern/trap.c 
void Trap_IRQ_0();
void Trap_IRQ_1();
void Trap_IRQ_2();
void Trap_IRQ_3();
void Trap_IRQ_4();
void Trap_IRQ_5();
void Trap_IRQ_6();
void Trap_IRQ_7();
void Trap_IRQ_8();
void Trap_IRQ_9();
void Trap_IRQ_10();
void Trap_IRQ_11);
void Trap_IRQ_12();
void Trap_IRQ_13();
void Trap_IRQ_14();
void Trap_IRQ_15();

	SETGATE(idt[IRQ_OFFSET + 0], 0, GD_KT, Trap_IRQ_0, 0  );
	SETGATE(idt[IRQ_OFFSET + 1], 0, GD_KT, Trap_IRQ_1, 0  );
	SETGATE(idt[IRQ_OFFSET + 2], 0, GD_KT, Trap_IRQ_2, 0  );
	SETGATE(idt[IRQ_OFFSET + 3], 0, GD_KT, Trap_IRQ_3, 0  );
	SETGATE(idt[IRQ_OFFSET + 4], 0, GD_KT, Trap_IRQ_4, 0  );
	SETGATE(idt[IRQ_OFFSET + 5], 0, GD_KT, Trap_IRQ_5, 0  );
	SETGATE(idt[IRQ_OFFSET + 6], 0, GD_KT, Trap_IRQ_6, 0  );
	SETGATE(idt[IRQ_OFFSET + 7], 0, GD_KT, Trap_IRQ_7, 0  );
	SETGATE(idt[IRQ_OFFSET + 8], 0, GD_KT, Trap_IRQ_8, 0  );
	SETGATE(idt[IRQ_OFFSET + 9], 0, GD_KT, Trap_IRQ_9, 0  );
	SETGATE(idt[IRQ_OFFSET + 10], 0, GD_KT, Trap_IRQ_10, 0  );
	SETGATE(idt[IRQ_OFFSET + 11], 0, GD_KT, Trap_IRQ_11, 0  );
	SETGATE(idt[IRQ_OFFSET + 12], 0, GD_KT, Trap_IRQ_12, 0  );
	SETGATE(idt[IRQ_OFFSET + 13], 0, GD_KT, Trap_IRQ_13, 0  );
	SETGATE(idt[IRQ_OFFSET + 14], 0, GD_KT, Trap_IRQ_14, 0  );
	SETGATE(idt[IRQ_OFFSET + 15], 0, GD_KT, Trap_IRQ_15, 0  );
env_alloc()///
e->env_tf.tf_eflags |= FL_IF;
 sched_halt()/
		"pushl $0\n"
		// Uncomment the following line after completing exercise 13
		"sti\n"
		"1:\n"
		"hlt\n"

在这里插入图片描述

(3) 处理时钟中断

  函数 user/spin 中子系统获取运行之后就不将控制权交给内核,我们需要编写时钟中断程序,迫使将控制权交给内核,然后我们才可以运行其他的进程。
  系统中已经有的lapic_initpic_init 函数,初始化时钟中断,我们仅需要完善发生中断之后的处理函数。

  • Exercise 14
    在这里插入图片描述
		case (IRQ_OFFSET + IRQ_TIMER):
			lapic_eoi();
			sched_yield();
			break;

在这里插入图片描述
在这里插入图片描述

这里强调一下,前面的实验一定要不出错,我之前因为在LAB 4 中,为了测试多CPU 的时候,从内核态切换到用户态解锁位置不同带来的影响,修改了env_run() 中解锁的位置,但是没有修改过来,刚好从那个实验之后我都没有测试过 make qemu CPUS=4 类似的命令,导致在这边 make grade 的时候,stresssched 一直不通过,报错误的原因是在 fork () 函数中的 pgfault(0 函数中的判读是否是写时复制 页面的时候出错,我一直以为是我这部分的内容错误了,来回检查,包括系统调用页面分配等,后来只能无意中发现,如果我单CPU 运行的话结果和理想的一样,可是我之前多CPU 是可以运行的,回去看我之前的实验笔记,看到 锁的部分,想起来我当时做的实验验证。当时想验证解锁位置不同的带来的影响,当时还没有得出很重要的结论,貌似上下一两条代码区别不大,可是在这个实验的时候,问题就很大了。我当时将解锁的位置放在了 代码 lcr3(PADDR(curenv->env_pgdir)); 前面是错误的,这里解锁的位置很关键,一定要注意。

2、进程间通信

  

  • Exercise 15
    在这里插入图片描述
    (1)sys_ipc_recv
static int sys_ipc_recv(void *dstva)
{
	// LAB 4: Your code here.
	if((uintptr_t)dstva < UTOP && ((uintptr_t)dstva & 0xFFF)!= 0)	
		return -E_INVAL;
	curenv->env_ipc_recving = 1;	
	curenv->env_ipc_from = 0;
	curenv->env_ipc_dstva = dstva;
	curenv->env_status = ENV_NOT_RUNNABLE;
	sched_yield();
	//panic("sys_ipc_recv not implemented");
	return 0;
}

(2)sys_ipc_try_send()

static int sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm)
{
	// LAB 4: Your code here.
	struct Env * recv = NULL;
	//int r = envid2env(envid, &recv, 0);
	if ((envid2env(envid, &recv, 0)) < 0) 		
		return -E_BAD_ENV;
	if(!recv->env_ipc_recving || recv->env_ipc_from != 0)
		
		return -E_IPC_NOT_RECV;
	if ((uintptr_t)srcva < UTOP) {
		if((uintptr_t)srcva & 0xFFF) 
		
			return -E_INVAL;
		pte_t *pte = NULL;
		struct PageInfo *page = page_lookup(curenv->env_pgdir, srcva, &pte);
		if(!page)
		
			return -E_INVAL;
		if ((*pte & (PTE_U | PTE_P)) == 0) 
		
			return -E_INVAL;
		//if ((*pte & ~) != 0) 
		if((perm & ~(PTE_U | PTE_P | PTE_AVAIL | PTE_W)) > 0) 
		
			return -E_INVAL;
		if (!(*pte & PTE_W) && (perm & PTE_W)) 
		
			return -E_INVAL;
		if (page_insert(recv->env_pgdir, page, recv->env_ipc_dstva, perm) < 0)
			return -E_NO_MEM;
		recv->env_ipc_perm = perm;
	}
	
	recv->env_ipc_recving = 0;
	recv->env_ipc_from = curenv->env_id;
	recv->env_ipc_value = value;
	recv->env_status = ENV_RUNNABLE;
	recv->env_tf.tf_regs.reg_eax = 0;
	return 0;
	//panic("sys_ipc_try_send not implemented");
}

(3)ipc_recv

int32_t ipc_recv(envid_t *from_env_store, void *pg, int *perm_store)
{
	// LAB 4: Your code here.	
	int err;
	if ((err=sys_ipc_recv(pg)) < 0) {
		if (from_env_store) *from_env_store = 0;
		if (perm_store) *perm_store = 0;
		return err;
	}
	if (from_env_store)
		*from_env_store = thisenv->env_ipc_from;
	if (perm_store) {
		if (pg) *perm_store = thisenv->env_ipc_perm;
		else *perm_store = 0;
	}
	return thisenv->env_ipc_value;
}

(4)ipc_send

void ipc_send(envid_t to_env, uint32_t val, void *pg, int perm)
{

	int err;
	while((err = sys_ipc_try_send(to_env, val, pg != NULL ? pg :(void *) UTOP, perm)) < 0 )
	{
		
		if(err != -E_IPC_NOT_RECV)//E_IPC_NOT_RECV
			
			panic("ipc_send error: %e", err);
		sys_yield();
	}
	//panic("ipc_send not implemented");
}

(5) make grade
在这里插入图片描述

实验完善代码 LAB2-4下载链接 提取码:79t8

分类 MIT6.828 32位操作系统实验笔记  

本文参考文章 http://grid.hust.edu.cn/zyshao/OSEngineering.htm

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值