1. 程式人生 > >Nachos作業系統課設 淺談優先順序排程

Nachos作業系統課設 淺談優先順序排程

題目

Task1.5 實現優先順序排程

  Implement priority scheduling in Nachos by completing the PriorityScheduler class. Priority scheduling is a key building block in real-time systems. Note that in order to use your priority scheduler, you will need to change a line in nachos.confthat specifies the scheduler class to use. The 

ThreadedKernel.scheduler key is initially equal to nachos.threads.RoundRobinScheduler. You need to change this to nachos.threads.PriorityScheduler when you're ready to run Nachos with priority scheduling.

  Note that all scheduler classes extend the abstract class nachos.threads.Scheduler. You must implement the methods getPriority()

,getEffectivePriority(), and setPriority(). You may optionally also implement increasePriority() and decreasePriority() (these are not required). In choosing which thread to dequeue, the scheduler should always choose a thread of the highest effective priority. If multiple threads with the same highest priority are waiting, the scheduler should choose the one that has been waiting in the queue the longest.

  An issue with priority scheduling is priority inversion. If a high priority thread needs to wait for a low priority thread (for instance, for a lock held by a low priority thread), and another high priority thread is on the ready list, then the high priority thread will never get the CPU because the low priority thread will not get any CPU time. A partial fix for this problem is to have the waiting thread donate its priority to the low priority thread while it is holding the lock.

  Implement the priority scheduler so that it donates priority, where possible. Be sure to implement Scheduler.getEffectivePriority(), which returns the priority of a thread after taking into account all the donations it is receiving.

  Note that while solving the priority donation problem, you will find a point where you can easily calculate the effective priority for a thread, but this calculation takes a long time. To receive full credit for the design aspect of this project, you need to speed this up by caching the effective priority and only recalculating a thread's effective priority when it is possible for it to change.

  It is important that you do not break the abstraction barriers while doing this part -- the Lock class does not need to be modified. Priority donation should be accomplished by creating a subclass of ThreadQueue that will accomplish priority donation when used with the existing Lock class, and still work correctly when used with the existing Semaphore and Condition classes. Priority should also be donated through thread joins.

  Priority Donation Implementation Details:

  1) A thread's effective priority is calculated by taking the max of the donor's and the recipient's priority. If thread A with priority 4 donates to thread B with priority 2, then thread B's effective priority is now 4. Note that thread A's priority is also still 4. A thread that donates priority to another thread does not lose any of its own priority. For these reasons, the term "priority inheritance" is in many ways a more appropriate name than the term "priority donation".

  2) Priority donation is transitive. If thread A donates to thread B and then thread B donates to thread C, thread B will be donating its new effective priority (which it received from thread A) to thread C.

淺說

        題目要求了要用較低的複雜度實現帶有優先順序傳遞的優先順序排程,這要求我們在更新一個執行緒的優先順序後要遞迴地將優先順序向上傳遞。但是優先順序的傳遞並不是一條鏈狀的,因為一個執行緒可以通過Lock類或者join()方法持有多個等待佇列。那麼我們再來考慮一個問題,優先順序的傳遞是否會構成環或者複雜的有向圖呢?不會,因為在Nachos系統中,一個執行緒acquire()一個鎖或者join()另一個執行緒後,就會立刻進入等待狀態,而不繼續執行其他操作,這就意味著一個執行緒不會處於多個等待佇列中,所以優先順序的傳遞關係實際上是一個有向森林,是樹形的。這裡我對一個執行緒用一個連結串列holdQueues來儲存了它所持有的全部等待佇列,用waitQueue來代表它所處的等待佇列,並在佇列的實現中設定了lockholder,來表示該佇列鎖的持有者,遞迴的實現在getEffectivePriority()中,每次程序修改優先順序就會去呼叫該方法,waitQueue的lockholder來向上更新全部的優先順序關係。

        另外一個問題就是如何在保證邏輯完備的情況下將排程高效地實現,這裡大多數的同學都沒有做對,他們犯了一個嚴重的錯誤,使用了java自帶的PirorityQueue或者TreeSet,但是在setPirority時沒有將執行緒從佇列中移出再放回,而是直接修改了優先順序,PirorityQueue是一個二叉堆,TreeSet是利用紅黑樹實現的,它們的形狀在節點插入或者移出時改變,只修改權值並不會引起結構的改變,修改其實是無效的。採用PirorityQueue的方法無疑是最糟糕的,因為這意味著你每次修改優先順序都需要使用Iterator遍歷整個佇列找到你要修改的執行緒。

        使用TreeSet可以在log(n)的複雜度內移出一個特定執行緒,已經比較好了,但還是會出現小問題,因為TreeSet是一個集合,裡面不能有重複的元素,而TreeSet是採用權重來區分執行緒的,這樣如果只將優先順序作為權重,相同優先順序的執行緒就會被覆蓋,所以我們要設法使每個執行緒權重均不相同。注意到要求中有這樣一句話,要求相同優先順序的程序先進入佇列的先出佇列,所以我們就可以將權重設定為一個雙關鍵字的值,先比較優先順序,再比較時間。如果你將進入佇列時系統的時鐘滴答作為時間,這樣看起來充分利用了系統特性,好像是個不錯的主意,但是你又搞錯了,因為nachos的時鐘滴答一般是在一個執行緒yield()時才會增加10個,這樣如果你一次性將多個執行緒放入就緒佇列的話,他們進入佇列的時鐘滴答實際是一樣的,Treeset同樣無法識別這些執行緒。好的做法是在佇列中設定一個計數器,每次執行緒呼叫waitForAccess()都會使該佇列的計數器加一,通過這個計數器來代表執行緒進入佇列的時間。

        能夠完整地將上面的做法實現已經很不錯了,但是演算法還可以進一步優化。意識到優先順序只有0~7這8個等級,我們可以在佇列中設定一個大小為8的TreeSet陣列,每個執行緒根據優先順序放入相應的TreeSet中。這樣的做法有一個好處,就是在更新一個執行緒的有效優先順序時,我們要查詢它所持有的全部等待佇列中執行緒優先順序的最大值,如果我們直接用一個TreeSet複雜度將是O(logn),當TreeSet中執行緒數量超過256個時,操作次數是大於8的,但是用剛才提到的做法我們只需要查佇列中的8個TreeSet的size()是否大於0即可,size()方法的複雜度是O(1)的,這樣不管有多少程序,對每個佇列的查詢操作次數都不會超過8,複雜度達到了常數級。

        在程式碼的實現中還有諸多細節,這裡不再一一贅述。

實現程式碼

package nachos.threads;

import nachos.machine.*;
import java.util.*;

public class PriorityScheduler extends Scheduler {
    public PriorityScheduler() {
    }
    
    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;
    }

    public static int priorityDefault = 1;
    public static int priorityMinimum = 0;
    public static int priorityMaximum = 7;    

    protected ThreadState getThreadState(KThread thread) {
        if (thread.schedulingState == null)
            thread.schedulingState = new ThreadState(thread);
    
        return (ThreadState) thread.schedulingState;
    }
    
    protected class PriorityQueue extends ThreadQueue {
        PriorityQueue(boolean transferPriority) {
            this.transferPriority = transferPriority;
            init();
        }
        public void init() {
            cnt=0;
            wait=new TreeSet[priorityMaximum+1];
            for(int i=0;i<=priorityMaximum;i++)
                wait[i]=new TreeSet<ThreadState>();
        }
    
        public void waitForAccess(KThread thread) {
            Lib.assertTrue(Machine.interrupt().disabled());
            getThreadState(thread).waitForAccess(this);
        }
    
        public void acquire(KThread thread) {
            Lib.assertTrue(Machine.interrupt().disabled());
            getThreadState(thread).acquire(this);
            if(transferPriority)
                lockholder=getThreadState(thread);
        }
    
        public KThread nextThread() {
            Lib.assertTrue(Machine.interrupt().disabled());
            ThreadState res=pickNextThread();
            
            return res==null?null:res.thread;
        }
    
        protected ThreadState pickNextThread() {
            ThreadState res=NextThread();
            
            if(lockholder!=null)
            {
                lockholder.holdQueues.remove(this);
                lockholder.getEffectivePriority();
                lockholder=res;
            }
            if(res!=null) res.waitQueue=null;
            return res;
        }
        
        protected ThreadState NextThread() {
            ThreadState res=null;

            for(int i=priorityMaximum;i>=priorityMinimum;i--)
               if((res=wait[i].pollFirst())!=null) break;
            
            return res;
        }
        
        public void print() {
            Lib.assertTrue(Machine.interrupt().disabled());
        }
        
        public void add(ThreadState state) {
            wait[state.effectivepriority].add(state);
        }
        
        public boolean isEmpty() {
            for(int i=0;i<=priorityMaximum;i++)
                 if(!wait[i].isEmpty()) return false;
            return true;
        }
    
        protected long cnt;
        public boolean transferPriority;
        protected TreeSet<ThreadState>[] wait;
        protected ThreadState lockholder=null;
    }

    protected class ThreadState implements Comparable<ThreadState>{
        public ThreadState(KThread thread) {
            this.thread = thread;
            holdQueues=new LinkedList<PriorityQueue>();
            
            setPriority(priorityDefault);
            getEffectivePriority();
        }

        public int getPriority() {
            return priority;
        }

        public int getEffectivePriority() {
            int res=priority;
            if(!holdQueues.isEmpty()) {
                Iterator it=holdQueues.iterator();
                while(it.hasNext())
                {
                    PriorityQueue holdQueue=(PriorityQueue)it.next();
                    for(int i=priorityMaximum;i>res;i--)
                        if(!holdQueue.wait[i].isEmpty()) { res=i;break;}
                }
            }
            if(waitQueue!=null&&res!=effectivepriority)
            {
                ((PriorityQueue)waitQueue).wait[effectivepriority].remove(this);
                ((PriorityQueue)waitQueue).wait[res].add(this);
            }
            effectivepriority=res;
            if(lockholder!=null)
                lockholder.getEffectivePriority();
            return res;
        }
    
        public void setPriority(int priority) {
            if (this.priority == priority)
                return;
            
            this.priority = priority;
            
            getEffectivePriority();
        }
    
        public void waitForAccess(PriorityQueue waitQueue) {
            Lib.assertTrue(Machine.interrupt().disabled());
            
            time=++waitQueue.cnt;
            
            this.waitQueue=waitQueue;
            waitQueue.add(this);
            lockholder=waitQueue.lockholder;
            getEffectivePriority();
        }
    
        public void acquire(PriorityQueue waitQueue) {
            Lib.assertTrue(Machine.interrupt().disabled());
             
            if(waitQueue.transferPriority) holdQueues.add(waitQueue);
            Lib.assertTrue(waitQueue.isEmpty());
        }   
       
        protected KThread thread;
        protected int priority,effectivepriority;
        protected long time;
        protected ThreadQueue waitQueue=null;
        protected LinkedList holdQueues;
        protected ThreadState lockholder=null;

        public int compareTo(ThreadState ts) {
            if(time==ts.time) return 0;
            return time>ts.time?1:-1;
        }
    }
}


測試

測試程式碼:

    private static class PingTest implements Runnable
    {
        Lock a=null,b=null;
        int name;
        PingTest(Lock A,Lock B,int x)
        {
            a=A;b=B;name=x;
        }
        public void run() {
            System.out.println("Thread "+name+" starts.");
            if(b!=null)
            {
                System.out.println("Thread "+name+" waits for Lock b.");
                b.acquire();
                System.out.println("Thread "+name+" gets Lock b.");
            }
            if(a!=null)
            {
                System.out.println("Thread "+name+" waits for Lock a.");
                a.acquire();
                System.out.println("Thread "+name+" gets Lock a.");
            }
            KThread.yield();
            boolean intStatus = Machine.interrupt().disable();
            System.out.println("Thread "+name+" has priority "+ThreadedKernel.scheduler.getEffectivePriority()+".");
            Machine.interrupt().restore(intStatus);
            KThread.yield();
            if(b!=null) b.release();
            if(a!=null) a.release();
            System.out.println("Thread "+name+" finishs.");
            
        }
    }
    
    public static void selfTest()
    {
        Lock a=new Lock();
        Lock b=new Lock();
        
        Queue<KThread> qq=new LinkedList<KThread>();
        for(int i=1;i<=5;i++)
        {
            KThread kk=new KThread(new PingTest(null,null,i));
            qq.add(kk);
            kk.setName("Thread-"+i).fork();
        }
        for(int i=6;i<=10;i++)
        {
            KThread kk=new KThread(new PingTest(a,null,i));
            qq.add(kk);
            kk.setName("Thread-"+i).fork();
        }
        for(int i=11;i<=15;i++)
        {
            KThread kk=new KThread(new PingTest(a,b,i));
            qq.add(kk);
            kk.setName("Thread-"+i).fork();
        }
        KThread.yield();
        Iterator it=qq.iterator();
        int pp=0;
        while(it.hasNext())
        {
            boolean intStatus = Machine.interrupt().disable();
            ThreadedKernel.scheduler.setPriority((KThread)it.next(),pp+1);
            Machine.interrupt().restore(intStatus);
            pp=(pp+1)%6+1;
        }
    }


結果:

        執行緒1~5不要任何鎖,執行緒6~10需要鎖a,執行緒11~15需要鎖a和鎖b,並將15個執行緒的優先順序設定如下:1 2 3 4 5 6 1 2 3 4 5 6 1 2 3,這樣根據優先順序傳遞的原則,執行結果如下:

Thread 1 starts.

Thread 2 starts.

Thread 3 starts.

Thread 4 starts.

Thread 5 starts.

Thread 6 starts.

Thread 6 waits for Lock a.

Thread 6 gets Lock a.

Thread 7 starts.

Thread 7 waits for Lock a.

Thread 8 starts.

Thread 8 waits for Lock a.

Thread 9 starts.

Thread 9 waits for Lock a.

Thread 10 starts.

Thread 10 waits for Lock a.

Thread 11 starts.

Thread 11 waits for Lock b.

Thread 11 gets Lock b.

Thread 11 waits for Lock a.

Thread 12 starts.

Thread 12 waits for Lock b.

Thread 13 starts.

Thread 13 waits for Lock b.

Thread 14 starts.

Thread 14 waits for Lock b.

Thread 15 starts.

Thread 15 waits for Lock b.

Thread 6 has priority 7.

Thread 6 finishs.

Thread 7 gets Lock a.

Thread 7 has priority 7.

Thread 7 finishs.

Thread 11 gets Lock a.

Thread 11 has priority 7.

Thread 11 finishs.

Thread 13 gets Lock b.

Thread 13 waits for Lock a.

Thread 5 has priority 5.

Thread 5 finishs.

Thread 4 has priority 4.

Thread 10 gets Lock a.

Thread 4 finishs.

Thread 10 has priority 4.

Thread 10 finishs.

Thread 13 gets Lock a.

Thread 13 has priority 7.

Thread 13 finishs.

Thread 12 gets Lock b.

Thread 12 waits for Lock a.

Thread 3 has priority 3.

Thread 9 gets Lock a.

Thread 3 finishs.

Thread 9 has priority 3.

Thread 9 finishs.

Thread 12 gets Lock a.

Thread 12 has priority 6.

Thread 12 finishs.

Thread 15 gets Lock b.

Thread 15 waits for Lock a.

Thread 2 has priority 2.

Thread 8 gets Lock a.

Thread 2 finishs.

Thread 8 has priority 2.

Thread 8 finishs.

Thread 15 gets Lock a.

Thread 15 has priority 3.

Thread 15 finishs.

Thread 14 gets Lock b.

Thread 14 waits for Lock a.

Thread 14 gets Lock a.

Thread 14 has priority 2.

Thread 14 finishs.

Thread 1 has priority 1.

Thread 1 finishs.

相關推薦

Nachos作業系統 優先順序排程

題目 Task1.5 實現優先順序排程   Implement priority scheduling in Nachos by completing the PriorityScheduler class. Priority scheduling is a key bu

作業系統——移動臂模擬排程演算法

最近學QT,哈哈哈,這兩天算是有點上路了,之前都是敲網上搜的小程式,這次作業系統課設準備用它寫,今天把介面和一個先來先服務功能實現了,有點小開心。 新東西,剛剛接觸覺得很難,但是認真去學的話其實也沒那磨難,程式碼還不完善,過兩天發

作業系統實驗五---Nachos檔案系統擴充套件

這次的實驗讓我想起了上學期被作業系統的九個實驗支配的恐懼,因為這次可能也是前五次試驗中最難的一次了,讀原始碼加實現花了可能一兩天的時間,所以也有必要記錄一下,有些地方做的不是很好,比如Makefile自己寫的話可能不需要把所有檔案都從filesys複製到lab5,可是我太菜做不到,所以先這樣

作業系統實驗一(Nachos上下文切換)

一、準備 1.32位系統的Linux 這裡我使用了32位的Ubuntu 16.04映象。 64位的我嘗試掙扎了一下不過還是跪了,而且我也沒有看到成功在64位Linux下跑成功過的博主。。 如果你是64位Linux,也可以考慮使用虛擬機器VMware workstation,是的,這個

作業系統實驗七---Nachos記憶體地址擴充套件

這次我們實驗要求是一直做到實驗七為止,但是比較難受的就是按照實驗七的要求記憶體地址空間擴充套件部分即使做完了也沒法驗證,因為如果想驗證就需要等到實驗八做完才能驗證,這裡為了通過增加部分程式碼的方式來對實驗七進行了驗證,很多實現也參考了寫完實驗八的那位學長的部落格。 山東大學作業系統課設實驗n

作業系統移動臂排程演算法

main #include "pcb.h" int main() { work(); getchar(); getchar(); }work.cpp#include "pcb.h" void work() { Pcb pcb[200]; int a[

nachos】山東大學作業系統實驗nachos系統(1):執行緒初探

實驗內容: 下載並編譯nachos之後(注意只能在32位linux下成功編譯) 1. 跟蹤執行nachos之後,觀察以下函式的執行 (a) 上下文切換函式 SWITCH() (b) 函式 ThreadRoot() 使用gdb執行nachos,回答一下

磁碟排程演算法

磁碟排程在多道程式設計的計算機系統中,各個程序可能會不斷提出不同的對磁碟進行讀/寫操作的請求。由於有時候這些程序的傳送請求的速度比磁碟響應的還要快,因此我們有必要為每個磁碟裝置建立一個等待佇列,常用的磁碟排程演算法有以下四種: 先來先服務演算法(FCFS), 最短尋道

作業系統時間片輪迴演算法+優先順序排程演算法python實現

# priority algorithm.優先順序排程演算法 + 時間片輪迴演算法python實現 from collections import deque import time import random class PCB: def __init__(s

置字體大小

屏幕 討論 json 設計師 back blog 博客 href 手機 淺談設置字體大小 前言 如果設置字體用的是[UIFontsystemFontOfSize:20.0];這種方式 看一下文檔會發現這裏的後邊的字體的參數單位是point即pt 那麽如果是UI設計

分散式排程服務

在JAVA開發領域,目前可以通過以下幾種方式進行定時任務: Timer:jdk中自帶的一個定時排程類,可以簡單的實現按某一頻度進行任務執行。提供的功能比較單一,無法實現複雜的排程任務。 ScheduledExecutorService:也是jdk自帶的一個基於執行緒池設計的定時任務類。

演算法和資料結構: 五 優先順序佇列與堆排序

在很多應用中,我們通常需要按照優先順序情況對待處理物件進行處理,比如首先處理優先順序最高的物件,然後處理次高的物件。最簡單的一個例子就是,在手機上玩遊戲的時候,如果有來電,那麼系統應該優先處理打進來的電話。 在這種情況下,我們的資料結構應該提供兩個最基本的操作,一個是返回最高優先

h5(html5)怎樣實現互動動畫開發?--h5互動動畫件的優勢。

目前很多互動課件,尤其幼兒類的互動課件以動畫和互動相結合的型別居多,越來越多的教育機構發現了這種課件對於幼兒的吸引力遠大於其他型別的課件,隨著flash逐漸被市場淘汰,動畫和互動相結合的html5跨平臺課件越來越受到老師們的青睞,html5動畫互動課件具有跨平臺適應性,能夠適應目前主流的絕大部分平臺

裝置驅動的作用與本質,有無作業系統Linux裝置驅動的區別

  一、驅動的作用 任何一個計算機系統的執行都是系統中軟硬體協作的結果,沒有硬體的軟體是空中樓閣,而沒有軟體的硬體則只是一堆廢鐵。硬體是底層基礎,是所有軟體得以執行的平臺,程式碼最終會落實為硬體上的組合邏輯與時序邏輯;軟體則實現了具體應用,它按照各種不同的業務需求而設計,滿足了使用

libgo 原始碼剖析(1. libgo簡介與排程

閒談 協程是一個很早的概念了,早些年的遊戲行業中已經大規模地在使用,像lua、go這些語言中的協程原語已經相對比較完善了,一般來說直接使用就好,但是在系統後臺開發上,出現的時間並不長。我是做C++方向的後臺開發,目前國內一些公司也開源了一些C++協程庫,但目前來說,還是在逐步完善的階段。最早接觸的C++協程

webgl第三-著色器

需要原始碼可以Q群:828202939 或者點選這裡  希望可以和大家一起學習、一起進步!!純手打!! 書籍是PDF電子檔,也在Q群裡,所有的課程原始碼在我上傳的資源裡面,本來想設定開源,好像不行! 如有錯別字或有理解不到位的地方,可以留言或者加微信15250969798,在下

工作流排程系統

轉自 http://ju.outofmemory.cn/entry/221885 每個公司都有自己的一套或者多套排程系統,從簡單到複雜,滿足各種定製化的需求。 Crontab任務排程 在沒有工作流排程系統之前,公司裡面的任務都是通

實時作業系統和分時作業系統

什麼是實時作業系統? 參見 Donal Gillies 在 Realtime Computing FAQ 中提出定義: 實時系統指系統的計算正確性不僅取決於計算的邏輯正確性,還取決於產生結果的時間。如果未滿足系統的時間約束,則認為系統失效。 一個實

32位和64位作業系統與記憶體的關係

比如說有這樣一個場景,你在書房裡面看書 你代表–支援32位的CPU和64位的CPU 書桌代表–記憶體 書架代表–硬碟 你從書架取出32本書放在書桌上面,書桌只能同時鋪開放32本書,你可以同時看這32本書獲取資訊,看完以後,你將這32本書放到書架,然後

linux字元備註冊

在呼叫cdev_add()函式向系統註冊字元裝置之前應該先呼叫:int register_chrdev_region(dev_t from,unsigned count,const char *name)函式為其分配裝置號,此函式可用:int alloc_chrdev_region(dev_t *dev,un