nachos 优先级调度算法

来源:互联网 发布:造价师网络课程 编辑:程序博客网 时间:2024/05/21 19:31
  1. 实验题目
    通过完成实现PriorityScheduler优先级调度策略。所有的调度程序都继承自Scheduler类,所以必须实现PriorityScheduler中的PriorityQueue类中的nextThread(),pickNextThread()函数和ThreadState类中的getEffectivePriority(),waitForAccess(PriorityQueue waitQueue),acquire(PriorityQueue waitQueue)这些方法。
  2. 实验要求
    在本次实验中,涉及到一个问题:线程的优先级倒置。Nachos已经为我们提供了一个调度算法——RR调度,即轮转调度法,采用简单的FIFO队列进行调度,即在队列前面的线程先执行,在队列后面的线程后执行,执行一段时间后,再次将线程加入队列进行调度。优先级调度的传统算法是:每一个线程都拥有一个优先级,最大是7,最小是0,默认是1。在进行优先级调度时,会选择优先级大的线程先执行,优先级小的线程后执行。但是这样会出现一个问题,优先级小的线程就很有可能一直执行不了。当readyQueue里不断有高优先级的线程加入,则低优先级的线程就可能会饿死。因此有了优先级转置这一解决办法。当然,还有这样一种情况,低优先级的线程在高优先级的线程中调用了join方法,高优先级的线程本应该尽快执行结束,却因为一个低优先级的线程而不得不等待,在这种情况下,也是需要优先级倒置来解决的。优先级倒置的定义:当出现一个优先级高的线程等待低优先级线程时,若存在另外的高优先级线程而导致低优先级线程永远不能执行的情况,该低优先级线程则会发生优先级翻转,即该低优先级的线程会得到它的waitQueue上的线程的最大优先级,从而实现优先级的倒置,设置自己的优先级为最大优先级,使自己能够尽可能快的执行,执行完毕后,唤醒waitQueue上的等待自己的线程,从而使它们尽可能快的执行,以防止出现低优先级线程饿死的情况。
  3. 实验关键代码
package nachos.threads;import nachos.machine.*;import java.util.LinkedList;import java.util.TreeSet;import java.util.HashSet;import java.util.Iterator;/** * A scheduler that chooses threads based on their priorities. * * <p> * A priority scheduler associates a priority with each thread. The next thread * to be dequeued is always a thread with priority no less than any other * waiting thread's priority. Like a round-robin scheduler, the thread that is * dequeued is, among all the threads of the same (highest) priority, the * thread that has been waiting longest. * * <p> * Essentially, a priority scheduler gives access in a round-robin fassion to * all the highest-priority threads, and ignores all other threads. This has * the potential to * starve a thread if there's always a thread waiting with higher priority. * * <p> * A priority scheduler must partially solve the priority inversion problem; in * particular, priority must be donated through locks, and through joins. *///优先级反转问题,通过lock和joinpublic class PriorityScheduler extends Scheduler {    /**     * Allocate a new priority scheduler.     */    public PriorityScheduler() {    }    /**     * Allocate a new priority thread queue.     *     * @param   transferPriority    <tt>true</tt> if this queue should     *                  transfer priority from waiting threads     *                  to the owning thread.     * @return  a new priority thread queue.     */    public ThreadQueue newThreadQueue(boolean transferPriority) {    return new PriorityQueue(transferPriority);    }    public int getPriority(KThread thread) {    Lib.assertTrue(Machine.interrupt().disabled());    return getThreadState(thread).getPriority();    }    public int getEffectivePriority(KThread thread) {    Lib.assertTrue(Machine.interrupt().disabled());    return getThreadState(thread).getEffectivePriority();    }    public void setPriority(KThread thread, int priority) {    Lib.assertTrue(Machine.interrupt().disabled());    Lib.assertTrue(priority >= priorityMinimum &&           priority <= priorityMaximum);    getThreadState(thread).setPriority(priority);    }    public boolean increasePriority() {    boolean intStatus = Machine.interrupt().disable();    KThread thread = KThread.currentThread();    int priority = getPriority(thread);    if (priority == priorityMaximum)        return false;    setPriority(thread, priority+1);    Machine.interrupt().restore(intStatus);    return true;    }    public boolean decreasePriority() {    boolean intStatus = Machine.interrupt().disable();    KThread thread = KThread.currentThread();    int priority = getPriority(thread);    if (priority == priorityMinimum)        return false;    setPriority(thread, priority-1);    Machine.interrupt().restore(intStatus);    return true;    }    /**     * The default priority for a new thread. Do not change this value.     */    public static final int priorityDefault = 1;    /**     * The minimum priority that a thread can have. Do not change this value.     */    public static final int priorityMinimum = 0;    /**     * The maximum priority that a thread can have. Do not change this value.     */    public static final int priorityMaximum = 7;        /**     * Return the scheduling state of the specified thread.     *     * @param   thread  the thread whose scheduling state to return.     * @return  the scheduling state of the specified thread.     */    protected ThreadState getThreadState(KThread thread) {    if (thread.schedulingState == null)        thread.schedulingState = new ThreadState(thread);    return (ThreadState) thread.schedulingState;    }    /**     * A <tt>ThreadQueue</tt> that sorts threads by priority.     */    protected class PriorityQueue extends ThreadQueue {        public ThreadState linkthread;//linkthread代表该线程的Schedulestate        public LinkedList<ThreadState> link;//与该队列直接相连的线程        private int index;        PriorityQueue(boolean transferPriority) {            this.transferPriority = transferPriority;            link=new LinkedList<ThreadState>();        }        public void waitForAccess(KThread thread) {    //      thread在等待资源            Lib.assertTrue(Machine.interrupt().disabled());            getThreadState(thread).waitForAccess(this);        }        public void acquire(KThread thread) {    //      thread获得了一个资源            Lib.assertTrue(Machine.interrupt().disabled());            getThreadState(thread).acquire(this);        }        public KThread nextThread() {    //      在等待队列linkList<ThreadState> link里选择一个有效优先级最大的线程去接收该资源,并将其移除            Lib.assertTrue(Machine.interrupt().disabled());            // implement me            print();//          System.out.println("##########################");            ThreadState temp,threadstate=null;            index=0;            int max=-1;            while((temp=pickNextThread())!=null)            {                if(max<temp.getEffectivePriority())                {                    max=temp.getEffectivePriority();                    threadstate=temp;                }            }            if(threadstate==null)                return null;            else                return link.remove(link.indexOf(threadstate)).thread;        }        /**         * Return the next thread that <tt>nextThread()</tt> would return,         * without modifying the state of this queue.         *         * @return  the next thread that <tt>nextThread()</tt> would         *      return.         */        protected ThreadState pickNextThread() {    //      返回等待队列里线程的ThreadState            // implement me            while(index<link.size())            {                index++;                return link.get(index-1);            }            return null;        }        public void print() {    //      打印所有的在等待队列里的线程,即等待资源的线程            Lib.assertTrue(Machine.interrupt().disabled());//          ThreadState state=null;//          for(int i=0;i<link.size();i++)//          {//              state=link.get(i);//              System.out.println(state.thread.getName()+"*****"+state.priority);//          }        }        /**         * <tt>true</tt> if this queue should transfer priority from waiting         * threads to the owning thread.         */        public boolean transferPriority;    }    /**     * The scheduling state of a thread. This should include the thread's     * priority, its effective priority, any objects it owns, and the queue     * it's waiting for, if any.     *     * @see nachos.threads.KThread#schedulingState     */    protected class ThreadState {//使用ThreadState来将thread和优先级绑定在一起    /**     * Allocate a new <tt>ThreadState</tt> object and associate it with the     * specified thread.     *     * @param   thread  the thread this state belongs to.     */    public ThreadState(KThread thread) {        this.thread = thread;        setPriority(priorityDefault);        waitQueue=new PriorityQueue(true);//存在优先级翻转现象,创建一个优先级队列    }    /**     * Return the priority of the associated thread.     *     * @return  the priority of the associated thread.     */    public int getPriority() {        return priority;    }    /**     * Return the effective priority of the associated thread.     *     * @return  the effective priority of the associated thread.     */    public int getEffectivePriority() {        //获得等待队列上的所有线程的最大优先级,得到最大优先级        // implement me        effctivepriority=-1;        for(int i=0;i<waitQueue.link.size();i++)        {//          从thread1的threadstate即schedulingState的waitQueue中选取线程进行优先级逆转,只有一个thread3            if(effctivepriority<waitQueue.link.get(i).getEffectivePriority())                effctivepriority=waitQueue.link.get(i).getEffectivePriority();        }//have a question        if(effctivepriority>priority)            setPriority(effctivepriority);        return priority;    }    /**     * Set the priority of the associated thread to the specified value.     *     * @param   priority    the new priority.     */    public void setPriority(int priority) {//优先级传递        if (this.priority == priority)        return;        this.priority = priority;        // implement me    }    /**     * Called when <tt>waitForAccess(thread)</tt> (where <tt>thread</tt> is     * the associated thread) is invoked on the specified priority queue.     * The associated thread is therefore waiting for access to the     * resource guarded by <tt>waitQueue</tt>. This method is only called     * if the associated thread cannot immediately obtain access.     *     * @param   waitQueue   the queue that the associated thread is     *              now waiting on.     *     * @see nachos.threads.ThreadQueue#waitForAccess     */    public void waitForAccess(PriorityQueue waitQueue) {//      将需要等待获得资源的线程加入一个等待队列等待调度,即将此线程的状态传入等待队列        // implement me        waitQueue.link.add(this);//将线程的ThreadState加入到优先级队列中        if(waitQueue.linkthread!=null&&waitQueue.linkthread!=this)//          如果linkthread!=null说明有与waitQueue直接相连的线程,且如果该线程不是this,就要把自己也添加到后续的ThreadState的link里            waitQueue.linkthread.waitQueue.waitForAccess(thread);//      用在得到有效优先级上/*waitQueue.linkthread.waitQueue.waitForAccess(thread);这句代码是为了在获取优先优先级的时候使用的 * waitQueue.link.add(this);只是将thread3加入到thread1的waitQueue中,但是在之后的geteffectivePriority中就无法得知在thread1上挂起 * 的线程有哪些,当然,也可以通过得到thread1的waitQueue,从而得知在thread1上挂起的线程,从而得到他们中的最大的优先级,但是过去麻烦 * 可以在ThreadState中声明一个waitQueue,用它来保存挂在thread1上的线程,这样在得到优先优先级时,会比较方便 * 每个等待队列创建时都会注册一个线程作为linkthread,一旦有一个线程挂在waitQueue等待队列上,那么这个线程一定要挂在linkthread的waitQueue上, * 这样在在发生优先级逆转的时候,可以通过thread1的schedulingState的waitQueue得到在thread1上挂起的线程 */    }    /**     * Called when the associated thread has acquired access to whatever is     * guarded by <tt>waitQueue</tt>. This can occur either as a result of     * <tt>acquire(thread)</tt> being invoked on <tt>waitQueue</tt> (where     * <tt>thread</tt> is the associated thread), or as a result of     * <tt>nextThread()</tt> being invoked on <tt>waitQueue</tt>.     *     * @see nachos.threads.ThreadQueue#acquire     * @see nachos.threads.ThreadQueue#nextThread     */    public void acquire(PriorityQueue waitQueue) {        //队列中的线程共同持有的一个锁,即每一个waitQueue队里都会有一个锁,同时后面的waitforaccess方法会使线程挂在linkthread的waitQueue上        // implement me        Lib.assertTrue(waitQueue.link.isEmpty());        waitQueue.linkthread=this;    }       /** The thread with which this object is associated. */        protected KThread thread;    /** The priority of the associated thread. */    protected int priority;    protected int effctivepriority;    protected PriorityQueue waitQueue;//等待该线程thread的线程队列,thread可以得到waitQueue上的最大优先级    }}
  1. 实验测试代码
public static void selfTest_priorityScheduler()    {        KThread thread1=new KThread(new Runnable()        {            public void run(){                for(int i=0;i<3;i++)                {                    currentThread.yield();//不放弃CPU会一直执行,直到结束                    System.out.println("I am thread1!!");                }            }        });        KThread thread2=new KThread(new Runnable(){            public void run()            {                for(int i=0;i<3;i++)                {                    currentThread.yield();                    System.out.println("I am thread2!!");                }            }        });        KThread thread3=new KThread(new Runnable()        {            public void run()            {                thread1.join();                for(int i=0;i<3;i++)                {                    currentThread.yield();                    System.out.println("I am thread3!!");                }            }        });        thread1.setName("thread1");        thread2.setName("thread2");        thread3.setName("thread3");        boolean intStatue=Machine.interrupt().disable();        ThreadedKernel.scheduler.setPriority(thread1,2);//此时的ThreadedKernel的scheduler是PriorityScheduler即优先级调度        ThreadedKernel.scheduler.setPriority(thread2,4);        ThreadedKernel.scheduler.setPriority(thread3,6);        Machine.interrupt().setStatus(intStatue);        thread1.fork();        thread2.fork();        thread3.fork();    }
  1. 关键代码分析
    本次实验中主要修改的类是PriorityScheduler类。Nachos本来就提供了这个类,里面还有两个内部类:PriorityQueue类和ThreadState类,这三个类的共同合作实现了线程优先级反转。在之前的Task1中,我们实现了join方法,在这里,我们采用join方法来模拟需要优先级倒置的情景。首先,对于每一个KThread,它都会有waitQueue,用来存储在它上面挂起的线程,在初始化KThread的时候,调用waitQueue.acquire(this)来为每一个KThread初始化它的schedulingState,并让该成员变量指向自身的ThreadState,为后面的优先级倒置做准备。例如:有3个线程,分别为thread1,thread2,thread3,它们的优先级分别为2,4,6,在thread3中调用thread1.join(),则在runNextThread()里进行线程切换时,会调用PriorityScheduler的nextThread方法来进行线程切换。如果没有优先级倒置,则执行顺序为thread3、thread2、thread1,如果存在优先级倒置,则执行顺序为thread1、thread3、thread2。在ThreadState中,需要再声明两个成员变量:PriorityQueue waitQueue和int effectivePriority。其中waitQueue是用来存储挂在该thread上的线程的ThreadState,以便在优先级倒置时,可以通过遍历该等待队列来拿到线程中的最大优先级,从而赋给thread的priority。在ThreadState中,需要修改的三个方法为waitForAccess(PriorityQueue waitQueue),acquire(PriorityQueue waitQueue),getEffectivePriority()方法,waitForAccess方法是将当前线程thread挂在调用join线程的waitQueue,以及如果这个线程的waitQueue的linkthread不为空,则将该线程的ThreadState也挂到该linkthread的waitQueue上,以便在getEffectivePriority()时,可以遍历ThreadState的waitQueue来拿到最大优先级。getEffectivePriority()是拿到该线程的有效优先级。要对该ThreadState的waitQueue进行遍历,拿到在waitQueue上的ThreadState,得到每一个ThreadState的有效优先级,如果该优先优先级比thread的有效优先级大,则设置当前线程的优先优先级为它,这样就会得到该线程的优先优先级。在PriorityQueue中声明了3个数据成员,link链表,用来存储挂在该优先级队列上的线程的ThreadState,linkthread是该优先级队列的“主人”,即挂在link链表上的线程的优先级都可以被该队列的“主人”拿到,并且进行比较,拿到最大的优先级。在PriorityQueue的方法中,需要修改的是pickNextThread()和nextThread()。其中pickNextThread()方法是从link链表里拿到一个ThreadState对象。nextThread()方法是比较从link链表里拿到的ThreadState的有效优先级,找到有效优先级最大的的线程的ThreadState,并返回该ThreadState的thread对象,同时将该ThreadState从link链表里删除。
    这样,thread1得到有效优先级时,便会遍历下面的waitQueue,查看有几个线程挂在thread1的等待队列上,得到等待队列上的线程的最大优先级,并赋值给thread1的priority,即可实现优先级倒置。
  2. 运行结果截图

    上图为实验结果截图,在测试程序中,创建了3个线程,分别为thread1,thread,thread3,优先级分别为2、4、6,它们的run方法都是输出自己的名字。一旦nachos系统实现了优先级倒置,在thread3里调用thread1.join,则thread1就会获得thread3的优先级,thread3阻塞,就绪队列里只有thread1和thread2两个线程,有效优先级分别为6、4,所以会先执行thread1,thread1执行完毕后,thread3回到就绪队列里,此时就绪队列里只有thread2和thread3,有效优先级分别为4、6,所以再执行thread3,最后执行thread2.由于Nachos系统本身默认的调度算法是轮转调度法,所以为了使系统使用优先级调度,需要修改proj1的配置文件nachos.conf,将里面的ThreadedKernel.scheduler属性的值改为nachos.threads.PriorityScheduler。
    下图为配置文件的截图:
0 0
原创粉丝点击