Nachos 开发说明
至此,nachos已经做完了project1,对于每个task的实现以及功能说明,下面会详细说明:
Task1:
Join()方法: 通过创建一个waitqueue等待队列,当fork了两个kThread A B之后,然后在A里调用B.join()的时候,先阻塞A,即把A加入到等待队列里面,让A睡眠,当B执行完的时候,程序Kthread类里面的runthread{begin(),run(),finish()}会自动执行finish函数,因此finish函数里面会唤醒等待队列里面的线程,调用runNextThread得以实现。这里声明了join_counter变量,每次同一个kThread调用会增一,确保了只能执行一次。
Task2:
Condition2()方法,设置锁和条件变量,其中,锁控制的是互斥访问,条件变量控制同步,实现sleep(),wake(),wakeAll()方法。Sleep()是先把当前的线程加入到等待队列里面去,然后释放锁,状态变为阻塞状态,直到它醒过来的时候再获得锁,wake()是唤醒该条件变量下至多一个线程,就是从等待队列移出来最前面的一个,把他的状态设置为就绪,wakeAll()是唤醒该条件变量下的所有线程,即就是把等待队列里面的所有线程都移出来。
Task3:
Alarm()有两个方法,TimeInterrupt()和waitUntil(),然后定义了一个内部类KThreadWakeTime,把每个线程和它的醒来时间绑在一起。声明了一个linkedList,每个线程的醒来时间是通过当前时间加上睡眠时间计算出来的,在waitUntil()方法中根据醒来时间的前后顺序把他们放入到一个KthreadWakeTime类的linkedlist里面去,然后在alarm类的构造函数中调用timeInterrupt()方法,判断当前该不该唤醒链表中的线程。
Task4:
Communicator,创建一个互斥锁,说者和听者的条件变量,数量不定,还要创建一个说者的说话队列,把说者要说的话放进去,当说者来了的时候,先获得锁,判断是否有听者,如果没有听者等待,就把说者的话存入list里面去然后让说者睡眠。如果有听者等待,就唤醒一个听者。对于听者,先获得锁,然后进行判断尝试唤醒speaker,如果没有说者在等待,就要把听者放入等待队列然后睡眠,如果有说者在等待,就要唤醒一个说者,将自己挂起以等待speaker准备好数据再将自己唤醒,然后传递消息,最后释放锁。
Task5:
Scheduler,里面有两个内部类,PriorityQueue和ThreadState,他们互相调用,每个线程都会有一个线程状态,在ThreadState里会有一个属性,是PriorityQueue类的实例waitQueue,在PriorityQueue里会有一个ThreadState类的linkedlist链表waitQueue,和一个ThreadState类的实例linkedthread。在使用的时候,先会设置每个线程的优先级,然后为每个线程的优先级队列注册一个主人,即当前的这个线程,并且把其他线程加入到自己的等待队列里面去,当需要优先级反转的时候,就会调用effectivepriority()方法,即在waitqueue里面找到一个最大的优先级,并把它复制给当前的线程。从而实现优先级反转。
在测试类里面,通过调用join()方法来实现优先级反转。具体实现过程如下:对于每一个kThread类的线程,它在初始化的时候,会分配一个waitqueue(注意是每一个线程,注意不是static的!),在PriorityScheduler的调度之下,这个等待队列有两个属性,一个是waitqueue<linkedlist>,另一个是linkedthread,当该线程的waitqueue没有调用acquire()时,相当于linkedthread为空,在调用了acquire()时,该线程的schedulingState(这是threadState的一个对象)的waitqueue中的linkedthread挂上了自己,代码:waitQueue.linkedthread = this;(他是getThreadState方法下面的),在schedulingState里面,有一个waitqueue,这个waitqueue又有两个属性,一个是waitqueue,另一个是linkedthread,现在都为空,在本测试类中,我定义了3个线程,分别为a,b,c,他们的优先级分别为2,4,6,他们都放在主线程(main)的ready队列里面,然后要挑选出优先级最高的拿来执行,通过递归,找到waitqueue.waitqueue中优先级最大的那个线程,应该是c,所以把c线程remove出去,在c中调用了a.join(),这时,相当于把c加到了a的waitqueue<list>里面去。执行waitForAccess()函数。
if(waitQueue.linkedthread!=null&& waitQueue.linkedthread!=this){
waitQueue.linkedthread.waitQueue.waitForAccess(this.thread);
}
然后a线程优先级就改为6,得以执行,将a线程remove出去,接着remove的是c,再remove a。即线程的执行顺序为a,c,b.
Task6:
Boat,一群人过河,有若干大人和小孩,每次船里最多载两个小孩或者是一个大人,要求所有人从O岛到M岛,所以要让2个小孩把船开过去,放下其中一个,再让一个小孩把船开回来,让一个大人过河,接着让对面的小孩把船开回来作为一次传递,实现运送一个大人,如此循环往复多次,直到把所有的大人都运过去,运送结束,最后所有的小孩都过河。要避免当大人过河之后,河对岸没有小孩出现的场景,这样大人相当于白跑一趟,因为船还是要回来的。设置一个锁和条件变量,条件变量设置孩子在O岛,孩子在M岛,大人在O岛,当不该它走的时候就调用sleep()函数,该他走的时候就wake()。同时为了方便判断该不该走,还应该引入变量孩子在O岛和M岛的数量,大人在M、O岛的数量,船在不在O岛等条件。
Project2里面共有4个task,具体实现功能如下: