Lab6:進程的調度

CPU調度

從就緒隊列中挑選下一個佔用CPU運行的進程,從多個可用CPU中挑選就緒進程可以使用的CPU資源算法

調度策略

比較調度算法的準則

  • CPU使用率
  • 吞吐量
  • 週轉時間
  • 就緒等待時間
  • 響應時間

吞吐量與延遲ide

  1. 低延遲:喝水的時候想要一打開水龍頭水就流出來函數

  2. 高帶寬:給游泳池充水時但願從水龍頭裏同時流出大量的水,而且不介意是否存在延遲code

處理機調度策略的響應時間目標

  1. 減小響應時間
  2. 減小平均響應時間的波動
  3. 增長吞吐量
  4. 減小等待時間

調度算法

先來先服務算法(First Come First Served, FCFS)

依據進程進入就緒狀態的前後順序排列,進程進入等待或結束狀態時,就緒隊列中的下一個進程佔用CPU隊列

可是FCFS的平均等待時間波動較大,I/O資源和CPU資源的利用率較低進程

短進程優先算法(SPN)

選擇就緒隊列中執行時間最短進程佔用CPU進入運行狀態,用歷史的執行時間來預估將來的執行時間,短進程優先算法具備最優平均週轉時間資源

可是連續的短進程流會使長進程沒法得到CPU資源rem

最高響應比優先算法(HRRN)

選擇就緒隊列中響應比R值最高的進程it

R=(w+s)/sio

w: 等待時間(waiting time)
    s: 執行時間(service time)

時間片輪轉算法(RR, Round-Robin)

利用時間片做爲分配處理機資源的基本時間單元,時間片結束時,按FCFS算法切換到下一個就緒進程, 每隔(n – 1)個時間片進程執行一個時間片q

可是時間片輪轉算法須要選擇好時間片的大小,過大太小都會致使效率問題

多級隊列調度算法(MQ)

就緒隊列被劃分紅多個獨立的子隊列,如:前臺–RR、後臺–FCFS

多級反饋隊列算法(MLFQ)

進程可在不一樣隊列間移動的多級隊列算法,時間片大小隨優先級級別增長而增長,如進程在當前的時間片沒有完成,則降到下一個優先級

公平共享調度(FSS, Fair Share Scheduling)

FSS控制用戶對系統資源的訪問,一些用戶組比其餘用戶組更重要,保證不重要的組沒法壟斷資源,未使用的資源按比例分配,沒有達到資源使用率目標的組得到更高的優先級

代碼實現

這個實驗其實有兩個,一個是實現Round Robin,一個是Stride Scheduling,兩個都很是簡單。

Round Robin調度算法的調度思想是讓全部 runnable 態的進程分時輪流使用 CPU 時間。Round Robin 調度器維護當前 runnable進程的有序運行隊列。當前進程的時間片用完以後,調度器將當前進程放置到運行隊列的尾部,再從其頭部取出進程進行調度。

Stride Scheduling

具體看一下Stride Scheduling

一、爲每一個runnable的進程設置一個當前狀態stride,表示該進程當前的調度權。另外定義其對應的pass值,表示對應進程在調度後,stride 須要進行的累加值。
二、每次須要調度時,從當前 runnable 態的進程中選擇 stride最小的進程調度。對於得到調度的進程P,將對應的stride加上其對應的步長pass(只與進程的優先權有關係)。
三、在一段固定的時間以後,回到步驟2,從新調度當前stride最小的進程

static void  
stride_init(struct run_queue *rq) {  
     /* LAB6: YOUR CODE */  
     list_init(&(rq->run_list)); 
     rq->lab6_run_pool = NULL; 
     rq->proc_num = 0;
}

首先是隊列初始化函數

static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
     /* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
     rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);
#else
     assert(list_empty(&(proc->run_link)));
     list_add_before(&(rq->run_list), &(proc->run_link));
#endif
     if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
          proc->time_slice = rq->max_time_slice;
     }
     proc->rq = rq;
     rq->proc_num ++;
}

而後是入隊函數stride_enqueue,根據以前對該調度算法的分析,這裏函數主要是初始化剛進入運行隊列的進程 proc 的stride屬性,而後比較隊頭元素與當前進程的步數大小,選擇步數最小的運行,即將其插入放入運行隊列中去,這裏並未放置在隊列頭部。最後初始化時間片,而後將運行隊列進程數目加一。

static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {
     /* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
     rq->lab6_run_pool = skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);
#else
     assert(list_empty(&(proc->run_link)));
     list_add_before(&(rq->run_list), &(proc->run_link));
#endif
     if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
          proc->time_slice = rq->max_time_slice;
     }
     proc->rq = rq;
     rq->proc_num ++;
}
static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc) {
     /* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
     rq->lab6_run_pool = skew_heap_remove(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f); //從優先隊列中移除
#else
     assert(!list_empty(&(proc->run_link)) && proc->rq == rq);
     list_del_init(&(proc->run_link));
#endif
     rq->proc_num --;
}
static struct proc_struct *
stride_pick_next(struct run_queue *rq) {
     /* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
     if (rq->lab6_run_pool == NULL) return NULL;
     struct proc_struct *p = le2proc(rq->lab6_run_pool, lab6_run_pool);
#else
     list_entry_t *le = list_next(&(rq->run_list));

     if (le == &rq->run_list)
          return NULL;

     struct proc_struct *p = le2proc(le, run_link);
     le = list_next(le);
     while (le != &rq->run_list)
     {
          struct proc_struct *q = le2proc(le, run_link);
          if ((int32_t)(p->lab6_stride - q->lab6_stride) > 0)
               p = q;
          le = list_next(le);
     }
#endif
    //更新對應進程的stride值
     if (p->lab6_priority == 0)
          p->lab6_stride += BIG_STRIDE;
     else p->lab6_stride += BIG_STRIDE / p->lab6_priority;   
     return p;
}

接下來就是進程的調度函數stride_pick_next,觀察代碼,它的核心是先掃描整個運行隊列,返回其中stride值最小的對應進程,而後更新對應進程的stride值,將步長設置爲優先級的倒數,若是爲0則設置爲最大的步長。

static void  
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) {  
     /* LAB6: YOUR CODE */  
    if (proc->time_slice > 0) 
    {  
        proc->time_slice --;  
    }  
    if (proc->time_slice == 0) 
    {  
        proc->need_resched = 1;  
    }  
}

最後是時間片函數stride_proc_tick,主要工做是檢測當前進程是否已用完分配的時間片。

相對於這兩個算法我以爲更重要的是明白進程的調度時機

  1. 時鐘中斷處理函數檢測到時間片到期了
  2. 發生阻塞或者睡眠等狀況
相關文章
相關標籤/搜索