nachos中的Alarm类的实现及测试方案

        Alarm类的功能为使用硬件计时器提供抢占,允许线程挂起一段确定的时间, 到达线程应该唤醒的时间后,将线程唤醒。

        在Alarm类中有Alarm()、timerInterrupt()、waitUntil(x)三个方法,三个方法实现的功能分别为:

        Alarm():设置机器的定时器中断处理程序来实现alarm的回调。

        timerInterrupt():计时器中断处理程序,这个方法周期性的被机器的时钟调用(大概500 ticks一次),让当前执行的进程让出CPU,如果有其他的线程应该执行,会发生强制上下文转换。

        waitUntil(x):让当前线程挂起,挂起的时间长度为x,在计时器中断处理程序中将挂起的程序唤醒,线程必须在不等式(current time) >= (WaitUntil called time)+(x)成立的第一个时钟中断期间被唤醒。

        Alarm类的工作过程为,某个进程调用waitUntil(x)后,这个进程将被挂起,理论上线程被唤醒的时间应该为当前时间+x。timerInterrupt()方法大约每500ticks执行一次,检查是否有应该被唤醒线程(线程应该被唤醒的条件为(current time) >= (WaitUntil called time)+(x)成立),如果有应该被唤醒的线程,则将其唤醒。

        基于Alarm类的工作过程,每个线程应该有一个被唤醒的时间属性wakeTime;Alarm类的对象应该有一个等待队列waitQueue,等待队列中存放的是因为调用waitUntil(x)方法被挂起的线程。

        waitUntil(x)方法中,要根据当前时间和传进来的参数,设置进程应该被唤醒的时间;然后将当前进程存入waitQueue, 为了查找应该唤醒的线程简单,插入线程时可以按照线程应该被唤醒的时间有序插入;插入后,应该将当前进程挂起,等待被timerInterrupt()唤醒。

        在timerInterrupt()方法中,要遍历waitQueue,将满足被唤醒条件(current time) >= (WaitUntil called time)+(x)的线程唤醒。

需要修改及添加的代码有:
KThread类中新增的全局变量:
public long wakeTime=0;//线程的等待时间,初始化为0
Alarm类:
package nachos.threads;
import java.util.LinkedList;
import nachos.machine.*;


public class Alarm {
    public Alarm() {
	Machine.timer().setInterruptHandler(new Runnable() {
		public void run() { timerInterrupt(); }
	    });
    }
    public void timerInterrupt() {
    	boolean status = Machine.interrupt().disable();
    	
    	KThread thread;
    	long currentTime = Machine.timer().getTime();//当前时间
    	int size=waitQueue.size();//得到等待队列的长度
    	//如果等待队列为空,return
    	if(size==0)
    		return;
    	//如果等待队列不为空,则将应该唤醒的线程唤醒
    	else{
    		for(int i=0;i<size;i++){
    			thread=waitQueue.get(i);
    			//如果索引为i的线程不应该被唤醒,那么其后的线程也不应该被唤醒
    			if(thread.wakeTime>currentTime)
    				break;
    			else{
    				thread.ready();
//    				System.out.println("实际被唤醒时间:"+currentTime);
    				waitQueue.remove(thread);
    				size--;
    				i--;					
    			}
    		}
    	}
    	Machine.interrupt().restore(status);
    }
    public void waitUntil(long x) {
    	boolean status = Machine.interrupt().disable();
    	long wakeTime = Machine.timer().getTime() + x;//线程应该被唤醒的时间
//    	System.out.println("理论被唤醒时间:"+wakeTime);
    	KThread.currentThread().wakeTime=wakeTime;//设置当前线程的wakeTime属性
    	int size=waitQueue.size();//等待队列长度
    	//将当前线程放入等待队列中,队列 为有序队列,应放入相应位置
    	if(size==0)
    		waitQueue.add(KThread.currentThread());
    	else{
    		for(int i=0;i<size;i++){
    			if(wakeTime<waitQueue.get(i).wakeTime){
    				waitQueue.add(i, KThread.currentThread());
    				break;
    			}
    			if(i==size-1)
    				waitQueue.add(size, KThread.currentThread());
    		}
    	}
    	KThread.currentThread().sleep();//将当前线程挂起
    	Machine.interrupt().restore(status);
    }
    LinkedList<KThread> waitQueue = new LinkedList();//有序的等待队列,存放的为调用了waitUntil方法的线程,前面的线程应该被唤醒的时间早
}
为了测试Alarm类,可以创建一个线程t,先让线程打印一下当前时间t1,然后调用waitUntil(x)方法;在waitUntil(x)方法中,打印一下线程理论上应该被唤醒的时间t2;在timerInterrupt()方法中,线程被唤醒后,打印一下当前时间t3;线程再次获得执行机会时,打印一下当前时间t4。理论上,这四个时间应满足的关系为:t2=t1+x t3>=t2 t4>t3。测试的代码为:
private static void alarmTest(){
    new KThread(new Runnable() {
		public void run() {
			System.out.println("当前时间:"+Machine.timer().getTime());
			ThreadedKernel.alarm.waitUntil(400);
			System.out.println("当前时间:"+Machine.timer().getTime());
		}
	}).fork();
}

可以得到的测试结果截图:
等待时间为400时
 
等待时间为500时
 
根据测试结果可以看出当等待时间为400 ticks时,理论被唤醒的时间与实际被唤醒的时间基本一致,但是当等待时间为500 ticks时,实际被唤醒的时间与理论唤醒的时间相差较大。这和Alarm类中唤醒进程的timerInterrupt()方法的工作机制有关,线程并不是到达应该被唤醒的时间后立即被唤醒,而是每隔大约500 ticks,发生计时器中断时,才有被唤醒的机会。只有当线程应该被唤醒和发生计时器中断两个条件都满足时,线程才能被唤醒。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值