OS之進程管理---進程調度和多線程調度

進程調度基本概念

多道程序的目標就是始終容許某個進程運行以最大化CPU利用率,多個進程通時存在於內存中,操做系統經過進程調度程序按特定的調度算法來調度就緒隊列中的進程到CPU,從而最大限度的利用CPU。算法

須要進行CPU調度的狀況能夠分爲四種:函數

  • 當一個進程從運行狀態切換到等待狀態時(如I/O請求,wait()調用以便等待一個子進程的結束)
  • 當一個進程從運行狀態切換到就緒狀態時(如出現了中斷)
  • 當一個進程從等待狀態切換到就緒狀態時(如I/O完成)
  • 當一個進程終止時

若是調度只能發生在第一種和第四種狀況下,那麼調度方案稱爲非搶佔的或協做的;不然調度方案就是搶佔的。性能

調度準則:this

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

進程調度算法

先到先服務調度(FCFS)

先請求CPU的進程首先分配到CPU。FCFS策略能夠經過FIFO隊列容易的實現,當一個進程進入就緒隊列時它的PCB被連接到隊列尾部。當CPU空閒時,它會分配給位於就緒隊列頭部的進程,並將這個進程從隊列中移除。
缺點:平均等待時間很長spa

進程 執行時間
P1P_1P1 24
P2P_2P2 3
P3P_3P3 3

若是上面三個進程順序到達,且按FCFS順序處理,那麼平均等待時間爲(0+24+27)/3=17(ms)
若是是:操作系統

進程 執行時間
P2P_2P2 3
P3P_3P3 3
P1P_1P1 24

那麼平均等待時間就是(6+0+3)/3=3(ms),因此進程的CPU執行時間變化波動很大的話,那麼採用這種調度策略的平均等待時間變化也會很大。
另外,考慮動態狀況下的FCFS調度性能,假設一個CPU密集型進程和多個I/O密集型進程隨着進程在系統中的運行,可能發生:CPU密集進程獲得CPU,並使用。在這段時間中,全部其餘進程會處理他們的I/O,並轉移到就緒隊列來進行等待CPU。當這些進程在等待時,I/O設備一直處於空閒狀態。當CPU密集型進程完成CPU執行並移到I/O設備,全部I/O密集型進程,因爲只有很短的cpu執行,因此很快執行完並一會到I/O隊列。這時CPU處於空閒。這種因爲其餘進程都等待一個大進程釋放CPU的現象叫作護航效果線程

注意,FCFS調度算法是非搶佔式的,一旦CPU分配給了一個進程,該進程就會使用CPU直到釋放爲止,因此FCFS不適用於分時系統。設計

最短做業優先調度(SJF)

該算法將每一個進程與下次CPU執行的長度關聯起來。當CPU空閒時,它會被賦給具備最短CPU執行的進程。若是兩個進程具備一樣長度的CPU執行,能夠有FCFS來處理。
能夠證實SJF調度算法是最優的。由於對於給定的一組進程,SJF算法的平均等待時間最小。經過將短進程移到長進程以前,短進程的等待時間減小大於長進程的等待時間增長,因此平均等待時間減小。
可是如何才能知道下次CPU執行的長度?對於批處理系統的長期調度,可將用戶提交做業時指定的進程時限做爲長度。SJF調度經常使用於長期調度。
雖然SJF是最優的,可是它不能在短時間CPU調度級別上加以實現,由於沒有辦法知道下次CPU執行的長度。一種方法是試圖近似SJF調度,經過計算下一個CPU執行長度的近似值,能夠選擇具備預測最短CPU執行的進程來運行。
下次CPU執行一般預測爲之前CPU執行的測量長度的指數平均。設tnt_ntn爲第n個CPU執行長度,設τn+1\tau_{n+1}τn+1爲下次CPU執行預測值。對於β\betaβ,0 ≤\leq β\betaβ ≤\leq 1,定義:指針

τn+1\tau_{n+1}τn+1 = β\betaβ tnt_ntn + (1 - β\betaβ)τn\tau_{n}τncode

tnt_ntn包括最近信息,而τn\tau_{n}τn存儲了過去歷史,參數β\betaβ控制最近和過去歷史在預測中的權重。若是β\betaβ=0,那麼 τn+1\tau_{n+1}τn+1τn\tau_{n}τn,最近歷史沒有影響(當前情形是瞬態);若是β\betaβ=1,那麼τn+1\tau_{n+1}τn+1=tnt_ntn ,只有最近CPU執行才重要(過去歷史被認爲是陳舊的,無關的)。通常狀況下,定義β\betaβ=12\frac {1}{2}21
爲了理解指數平均行爲,經過替換τn\tau_{n}τn,能夠展開τn+1\tau_{n+1}τn+1,從而獲得:

τn+1\tau_{n+1}τn+1=β\betaβtnt_ntn + (1 - β\betaβ)β\betaβtn−1t_{n-1}tn1 + … + (1−β)j(1 -β)^j(1β)jβtn−jt_{n - j}tnj + …+ (1−β)n+1(1 -β)^{n + 1}(1β)n+1τ0\tau_{0}τ0

一般β\betaβ和(1 - β\betaβ)小於1,因此後面項的權重值比前面項的權重要小。

SJF算法能夠是搶佔的也能夠是非搶佔的。新進程的下次CPU執行,與當前運行進程的還沒有完成的CPU執行相比,可能還要小,搶佔SJF算法會搶佔當前運行進程,而非搶佔SJF算法會容許當前運行進程以先完成CPU執行。搶佔SJF調度有時稱爲最短剩餘時間優先調度
例子

進程 到達時間 執行時間
P1P_1P1 0 8
P2P_2P2 1 4
P3P_3P3 2 9
P4P_4P4 3 5

按照SJF調度,這個例子的平均等待時間是[(10 - 1) + (1 - 1) + (17 - 2) + (5 - 3)]/4=26/4=6.5ms。若是使用非搶佔SJF調度算法,平均等待時間爲7.75ms。

優先級調度

SJF算法就是經過優先級調度算法的一個特例,每一個進程都有一個優先級與之關聯,而具備最高優先級的進程會被分配到CPU,具備相同優先級的進程按照FCFS順序調度。
優先級的定義能夠是分爲內部的或外部的。內部定義的優先級採用一些測量數據來計算進程優先級。外部定義的優先級採用操做系統以外的準則,如進程重要性等。
優先級調用能夠是搶佔式的也能夠是非搶佔式的。對於搶佔式的那麼較高優先級進程來時會搶佔CPU;對於非搶佔式的,只是將新進程加到就緒隊列的頭部。

優先級調度算法的一個主要問題就是無窮堵塞飢餓。即讓某個低優先級進程無窮等待CPU。常見的解決方式是老化,老化逐漸增長在系統中等待很長時間的進程的優先級。

輪轉調度(RR)

輪轉調度算法是專門爲分時系統設計德爾,相似與FCFS調度,並增長了搶佔以切換進程。將一個較小的時間單元定義爲時間量或時間片。時間片的大小一般爲10ms-100ms。就緒隊列做爲一個循環隊列,CPU調度程序循環整個就緒隊列,爲每一個進程分配不超過一個時間片的CPU。
在RR調度算法中,沒有進程會被連續分配超過一個時間片的CPU(除非是惟一可運行的進程),若是進程的CPU執行超過了一個時間片,那麼該進程會被搶佔,並被放回就緒隊列中,因此RR調度算法是搶佔的。
RR調度算法的性能很大程度上取決於時間片的大小,若是時間片很是大,那麼RR算法與FCFS算法同樣,若是時間片很小,那麼會形成大量的上下文切換。若是上下文切換時間約爲時間片的10%,那麼越10%的CPU時間會被浪費在上下文切換上,上下文切換的時間通常少於10ms。

多級隊列調度

多級隊列調度算法就是將就緒隊列分紅多個單獨隊列,根據進程屬性(如內存大小,進程優先級等)將每一個進程永久分到一個隊列中,每一個隊列有本身的調度算法。此外,隊列之間應該頁存在調度,一般採用固定優先級搶佔調度。

多級反饋隊列調度

在多級隊列調度算法中,進程被永久的分配給一個隊列,這種設置的優勢是調度開銷小,缺點是不夠靈活。
多級反饋隊列調度算法容許進程在隊列之間遷移。這種想法是根據不一樣CPU執行的特色來區分進程,若是進程使用過多的CPU時間,那麼將會被遷移到更低優先級的隊列。這種方案將I/O密集型和交互進程放在更高優先級隊列上。此外在較低優先級隊列中等待過長的進程會被一到更高優先級隊列中,這種形式的老化阻止飢餓的發生。
一般狀況下,多級反饋調度程序可由下列參數來進行定義:
隊列的數量
每一個隊列的調度算法
用以肯定什麼時候升級到更高優先級隊列的方法
用以肯定什麼時候降級到更低優先級隊列的方法
用以肯定進程在須要服務時將會進入哪一個隊列中的方法

線程調度

在支持線程的操做系統上,內核級線程纔是操做系統所調度的。用戶級線程是由線程庫來進行管理的,而內核並不知道他們。用戶級線程爲了運行在CPU上最終應映射到相關的內核級線程上,這種映射不是直接的,可能採用輕量級進程(LWP)
用戶級線程和內核級線程的一個區別就是他們如何調度的。
對於多對一和多對多模型的系統線程庫會調用用戶級線程,以便在可用的LWP上運行。這種方案成爲進程競爭範圍(PCS),由於競爭CPU是發生在同一進程的線程之間。
爲了決定哪一個內核級線程調度到一個處理器上,內核採用系統競爭範圍(SCS)。採用SCS調度來競爭CPU,發生在系統內全部線程之間。採用一對一模型的系統,如Window、Linux、Solaris,只採用SCS調度。
一般狀況下,PCS一般採用優先級調用,即調度程序選擇具備最高優先級的,可運行的線程,且容許一個更高優先級的線程來搶佔當前運行的線程。

Pthreads調度

在經過POSIX Pthreads來建立線程時容許指定PCS或SCS。Pthreads採用以下競爭範圍的值:

PTHREAD_SCOPE_PROCESS :按PCS來調度線程
    PTHREAD_SCOPE_SYSTEM:按SCS來調度線程

對於實現多對多模型的系統,PTHREAD_SCOPE_PROCESS策略調度用戶級線程可用LWP,LWP的數量經過線程庫來維護。PTHREAD_SCOPE_SYSTEM調度策略會建立一個LWP,並將多對多模型系統的每一個用戶級線程綁定到LWP,實際採用一對一策略來映射線程。
Pthreads IPC提供兩個函數,用來獲取和設置競爭範圍策略:

pthread_attr_setscope(pthread_attr_t *attr, int scope)
    pthread_attr_getscope(pthread_attr_t *attr, int *scope)

這兩個函數第一個參數是包含線程屬性值的指針,pthread_attr_setscope()第二個參數的值是PTHREAD_SCOPE_PROCESS或PTHREAD_SCOPE_SYSTEM
,pthread_attr_getscope()第二個參數的值是int值的指針,用於獲取競爭範圍的當前值。
示例:

#include <stdio.h>
#include <pthread.h>
#define NUM_THREADS 5


void *runner(void *param);

int main(int argc, char *argv[])
{
    int i, scope;
    pthread_t tid[NUM_THREADS];
    pthread_attr_t attr;

    pthread_attr_init(&attr);

    if(pthread_attr_getscope(&attr, &scope) != 0) {
        fprintf(stderr, "Unable to get scheduling scope\n");

    }
    else {
        if(scope == PTHREAD_SCOPE_PROCESS) {
            printf("PTHREAD_SCOPE_PROCESS\n");
        }
        else if(scope == PTHREAD_SCOPE_SYSTEM) {
            printf("PTHREAD_SCOPE_SYSTEM\n");
        }
        else {
            fprintf(stderr, "Illegal scope value.\n");
        }

    }

    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

    for(i = 0; i < NUM_THREADS; i++) {
        pthread_create(&tid[i], &attr, runner, NULL);
    }

    for(i = 0; i < NUM_THREADS; i++) {
        pthread_join(tid[i], NULL);
    }

    return 0;
}

void *runner(void *param)
{
    int i;
    for(i = 0; i<500000; i++) {};

    printf("this is thread\n");
    pthread_exit(0);

}
相關文章
相關標籤/搜索