Nachos中断处理时机;所以,在Nachos中只有在时钟前进时,才会检查;在有些中断处理程序的最后可能要进行正文切换;在这个中断系统基础上,Nachos模拟了各种硬件;
Timer类模拟定时器;
Timer类模拟定时器。定时器每隔X个时钟周期就向CPU发一个时钟中断。它是时间片管理必不可少的硬件基础。它的实现方法是将一个即将发生的时钟中断放入中断队列,
到了时钟中断应发生的时候,中断系统将处理这个中断,在中断处理的过程中又将下一个即将发生的时钟中断放入中断队列,这样每隔X个时钟周期,就有一个时钟中断发生。
由于Nachos是一个软件模拟的系统,有很多的随机事件需要通过一定的控制来实现。所以系统中在计算下一个时钟中断应发生的时间时,还加入了一些随机值,使得中断发
生的时间间隔不确定,这样就与现实的定时器更相似。
这样我们就可以知道,Nachos内部有自己的始终定时器,而且还有一个随机制度,导致可能会出现实际与计算值的偏差。
在了解了内部的时钟中断方式之后,我们再来看这个题目,分析一下:
实现这种通过时间控制线程沉睡的方式主要是增加相应的参数。通过时钟进行判断。在等待队列中,每个除了可以被wake()唤醒之外,还能够根据自己所存储的时间来进
行判断是不是该苏醒加入就绪队列等待执行。这个判断可以每隔一定的时间就循环遍历等待队列来做出。定时器中断处理程序被称为机器计时器定期(大约每500时钟周期)。当
前线程产生后,如果有另一个必须运行的线程,则强制上下文切换。
当前线程睡眠至少x时间周期,在定时器中断处理程序中将其唤醒。当现在时间(current time) >= (WaitUntil called time)+(x)时,线程必须被唤醒(在调度准备集)在
第一个定时器中断的产生时。
这样我们可以订立初步的方案:
在创建线程的时候将时间进行保存,然后将时间和线程存到一起,放入等待队列,然后每隔一定的时钟中断检查一次等待队列是不是有时间满足的,如果有,就放到就绪队
列等待执行。这样就实现了整个实验的情况
主要修改的是Alarm类,其中首先是要利用nachos系统中每隔一定时间就会回调执行的函数体:Alarm.timerInterrupt();这个方法会每隔一定的时间调用一次。这样,我们将
判断等待队列中线程等待时间的函数方法写在里面,就可以在每隔一段时间遍历后,将超过等待时间的线程从等待队列中取出来放置到就绪队列中。 waitUntil()方法使用了一个队列可以存放线程以及唤醒时间,这样在线程初始化的时候能够将相应的唤醒时间一起加入到这个等待队列中,等到可以唤醒的时间,再进行调用
wake()方法将能够唤醒的线程加入就绪队列。 Waiter()内部类定义了一个数据结构,该数据结构就是用来存放Kthread类的线程和这个线程等待时间的。
这样我们就能够完成这个实验了:
package nachos.threads; import java.util.LinkedList; import java.util.ListIterator; import nachos.machine.*; public class Alarm { public Alarm() { Machine.timer().setInterruptHandler(new Runnable() { public void run() { timerInterrupt(); } }); } public void timerInterrupt() {// 产生时间中断,将时间符合的线程放入ready队列以便执行 boolean status = Machine.interrupt().disable(); long currenttime = Machine.timer().getTime(); int size = linkedlist.size(); if (size == 0) ; else for (int i = 0; i < size; i++) { if (currenttime < linkedlist.get(i).getWakeTime()) ; else { KThread thread = linkedlist.get(i).getThread(); thread.ready(); linkedlist.remove(i); size--; i = 0; currenttime = Machine.timer().getTime(); } } KThread.currentThread().yield(); Machine.interrupt().restore(status); } public void waitUntil(long x) {// 将线程与时间结合放入一个有序链表,线程休眠 boolean status = Machine.interrupt().disable(); long waketime = Machine.timer().getTime() + x; KThreadWakeTime kthreadwaketime = new KThreadWakeTime( KThread.currentThread(), waketime); int size = linkedlist.size(); if (size == 0) linkedlist.add(kthreadwaketime); else for (int i = 0; i < size; i++) { if (waketime < linkedlist.get(i).getWakeTime()) { linkedlist.add(i, kthreadwaketime); break; } if (i == size - 1 && waketime >= linkedlist.get(i).getWakeTime()) linkedlist.add(i + 1, kthreadwaketime); } KThread.currentThread().sleep(); Machine.interrupt().restore(status); } public class KThreadWakeTime { private KThread thread = null; private long waketime = 0; public KThreadWakeTime(KThread thread, long waketime) { this.thread = thread; this.waketime = waketime; } public KThread getThread() { return thread; } public long getWakeTime() { return waketime; } } LinkedList<KThreadWakeTime> linkedlist = new LinkedList(); }
这是对Alarm的修改,大家可以自行了解参照。
接下来就讨论一下重点内容,并不是通常遇到的计算好的唤醒时间和实际唤醒时间不一样的情况。大家要注意,这里面的时间单位是Nachos自己定义的,所以具体与现实时间怎么对比
2000约1秒