進程調度

進程調度

一、什麼時候須要進程調度

  隨着CPU的處理速度的加快,如今計算密集型的程序已經很少了,大量的時間仍是浪費在了IO操做上了,即主要的程序類型從計算密集型向I/O密集型轉變,那麼進程的調度的決策主要出如今下面幾種狀況:算法

  一、在建立一個新的進程後,須要先運行父進程仍是先運行子進程,這個須要操做系統來定奪。數據庫

  二、在一個進程退出時,必需要作出調度。數組

  三、當一個進程阻塞在I/O和信號量上或者其餘緣由阻塞的時候,必需要進行進程調度。多線程

  四、在一個I/O中斷產生的時候。併發

  五、在時鐘中斷產生的時候,須要操做系統決定是否須要進行進程的切換。操作系統

二、調度算法

2.一、批處理系統的調度

一、先來先服務(FCFS).net

  主要的流程是CPU根據他們請求CPU的順序進行調度,而且爲非搶佔式的,當某個做業先提交的時候其便擁有了CPU,其會一直向後運行,直到發生阻塞即將其後面的進程分配CPU,在操做系統的內部維護一個就緒隊列和一個阻塞隊列,被阻塞的進程會放入阻塞線程

隊列中,直到阻塞隊列中的某些進行變爲就緒的時候會被從新放到隊列的尾部,好像剛剛到達同樣。blog

  優點在於便於理解比較公平,問題在於某一個進程須要1s的計算和0.01s的IO操做,另外的進程堆須要100s的IO操做,這樣該線程因爲這0.01s的IO操做會被堵住100s,很是不利於CPU密集型的進程。一樣的在調度的時候長做業會得到大量的CPU資源隊列

段做業相對而言沒有,有利於長做業不利於短做業。

二、最短做業優先

  估計各個做業的時間長度,按照時間從短到長進行調度,也是非搶佔式的。

  有利於短做業,即短做業的週轉時間(從提交到結束做業的時間間隔)會獲得下降,長做業的週轉時間也沒有特別多的提高。可是如何估計一個時間的長短比較困難,同時在做業不斷提交的過程當中,長做業有可能一直等待下去,不利於長做業。

三、最短剩餘時間優先

  最短做業優先的搶佔版本就是最短剩餘時間優先,問題依舊和最短做業優先差很少。

2.2 交互式系統中的進程調度

一、輪轉調度

  用的最多的一種制度,每一個進程被分配一個時間段,被稱爲時間片,即容許進程在該時間片內運行,若是時間片結束時該進程還在運行,則將剝奪CPU並分配給另外的進程,若是某個進程在時間片前阻塞或者結束,則CPU當即切換

  問題在於時間片長短的設置,過短的話CPU花太多的時間在上下文切換上,太長的話整個系統的實時性又大打折扣,一般設置爲20~50ms。

二、優先級調度

  爲每一個進程分配一個優先級,容許優先級最高的可運行程序先運行,優先級可用由認爲設定爲靜態優先級,也可讓系統動態設置。

三、多級隊列

  其設立優先級類,屬於最高優先級類的進程運行1個時間片,屬於次高優先級的進程運行2個時間片,再下一級使用4個時間片,以此類推。當一個進程加入隊列後其擁有最給的優先級,再運行完時間片後插入到次優先級進行隊列尾部,CPU選擇

當前最高優先級的進程運行。

  可以兼顧短的進程和長的進程。

2.3 實時系統的進程調度

  實時系統是一種時間起着主導做用的系統,其在書中沒有提到固定的算法,可是有一個規則須要把握才能表示該系統是可用調度的,即,其中n爲事件數量C爲cpu處理一個事件的時間,P爲該事件的發生週期,N爲CPU的核心數

  

3 、線程的調度

  因爲線程有兩種類型:用戶級線程和內核級的線程,內核級的線程,內核級的線程對操做系統是不可見的,操做系統可見的是線程所在的進程,這也是爲何用戶級的操做系統支持在非多線程的CPU上運行的緣由,此時在用戶級的進程中會存在一個本身的

線程調度算法在操做系統層面上也會存在一個全局的線程調度算法。因爲用戶級的線程是有單人編寫的,所以對於搶佔的問題不存在,更多的是合做。

  內核級的線程調度算法,他不用考慮線程屬於哪一個線程,相應的線程可用被阻塞,而不用擔憂用戶級線程的非阻塞調用的問題,可是內核級線程相對於進程級的線程的問題在於內核級的上下文切帶來了大量的時間消耗。還有一個區別是用戶級的線程可用定製本身的線程調度算法,可是非阻塞調用依舊是其問題。

四、經典的IPC問題

4.1 哲學家問題

  假設有五位哲學家圍坐在一張圓形餐桌旁,作如下兩件事情之一:吃飯,或者思考。吃東西的時候,他們就中止思考,思考的時候也中止吃東西。餐桌中間有一大碗意大利麪,每兩個哲學家之間有一隻餐叉。由於用一隻餐叉很難吃到意大利麪,因此假設哲學家必須用兩隻餐叉吃東西。他們只能使用本身左右手邊的那兩隻餐叉。哲學家就餐問題有時也用米飯和筷子而不是意大利麪和餐叉來描述,由於很明顯,吃米飯必須用兩根筷子。

  可使用規定只有左右兩個筷子同時可用的時候才進行就餐,不然放棄本身的資源,可是若是5個哲學家同時就餐那就出現了死鎖,可用在沒法拿到筷子的狀況下隨機延遲再從新拿,可是再某些不能出現死鎖的狀況下是不能夠的。如下代碼可用有效的處理哲學家問題

#define N
#define left (i+N-1)%N
#define right (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2
typedef int semaphore;
int state[N];//記錄各個哲學家當前處於的狀態(3種)
semaphore mutex=1;//臨界區鎖
semaphore s[N];//髮卡器,對具有吃條件的線程髮卡,不具有的條件的就等着
void philosopher(int i)
{
while(TRUE)
{
}
}
void take_forks(int i)
{
down(&mutex);
state[i]=HUNGRY;
test(i);
up(&mutex);
down(&s[i]);
}
void put_forks(int i)
{
down(&mutex);
state[i]=THINKING;
test(LEFT);
test(RIGHT);
up(&mutex)
}
void test(i)
{
if(state[i]==HUNGRY&&state[LEFT]!=EATING&&state[RIGHT]!=EATING)
{
state[i]=EATING;
up(&s[i]);
}
}

  在上述的解法種主要的是s[i]這個數組,經過這個數組咱們可用實現對不具有吃的條件的可是想吃的哲學家發個掛起卡(state[i]=HUNGRY&&s[i]=0),同時在結束吃的時候須要喚醒和其搶資源的哲學家,二喚醒的依據就是被髮掛起卡了,此時被掛起的線程就能夠運行了。

4.2 讀者-寫者問題

  有讀者和寫者兩組併發線程,共享一個數據庫,當兩個或以上的讀線程同時訪問共享數據時不會產生反作用,但若某個寫線程和其餘線程(讀線程或寫線程)同時訪問共享數據時則可能致使數據不一致的錯誤。所以要求[1]

一、容許多個讀者能夠同時對文件執行讀操做;
二、只容許一個寫者往文件中寫信息;
三、任一寫者在完成寫操做以前不容許其餘讀者或寫者工做;
四、寫者執行寫操做前,應讓已有的讀者和寫者所有退出。
  也就是說,讀進程不排斥其餘讀進程,而寫進程須要排斥其餘全部進程,包括讀進程和寫進程。在這個問題中就存在一個讀者優先仍是寫者優先的問題,即,當一個進程在讀的時候,後續的讀是否該超越後續的寫進程優先被執行仍是後續的寫先行被調度呢?

這兩中有各自的實現。

4.2.1讀者優先
typedef int semaphore;
semaphore mutex=1;//該互斥量用來實現對讀者數量的互斥,由於該變量對於判斷是否寫是否重要
semaphore db=1;//該互斥量用來進行對讀寫者的互斥調用,即讀時禁止寫
int rc=0;//記錄當前讀者的數量,由於數量爲1的話說明這個進程開啓了讀,此時須要設置db
void read()
{
while(TRUE)
{
down(&mutex);
rc+=1;
if(rc==1)
  down(&db)
up(&mutex) 
read_data();
down(&mutex)
rc-=1;
if(rc==0)
    up(&db)//當前沒有讀者了 
up(&mutex);
use_data();
}
}
void writer()
{
while(TRUE)
{
down(&db)
writer();
up(&db);
} }
4.2.2寫者優先

  相對於讀者優先中存在一個問題,就是寫者沒法致使讀者的長時間掛起,也就是說,當寫者完成後第二個寫準備就緒的時候有可能被讀者搶走了優先權。爲了處理這種狀況,須要對寫者進行改動,讀者不變,此時須要一個變量來記錄寫者的數量,這樣使用相似於讀者的思路就能夠實現寫者的優先搶佔,下面是個人第一個想法,實際上是錯誤的,在讀者與寫者都在等待的時候並不會將臨界區的全部權給寫者。

semaphore  w_mutex,w_lock;
int wc;
void writer()
{
while(TRUE)
{
down(&w_mutex);//與同時寫wc 的造成互斥
wc+=1;
up(&w_mutex);
if(wc==1)//>1的時候依據獲取了全部權
    down(&db);//獲取數據庫全部權  
down(&w_lock)//不可以同時寫
write();
up(&w_lock)//寫完成
down(&w_mutex);//與同時寫wc 的造成互斥
wc-=1;
up(&w_mutex);
if(wc==0)//=0時候,沒有寫了,交出全部權
    up(&db);//交出全部權  

}

}

  主要的緣由是,雖然在讀所有完成後,寫操做可用緊緊把握全部權,可是核心問題在於,在讀的過程當中依舊可讓新的讀進程超越寫而註冊到讀中,處理的辦法是加一把鎖,讓後於寫進程的進行沒法進行讀數量+1的操做,所以上述的代碼應該這樣寫:

讀者:

typedef int semaphore;
semaphore mutex=1;//該互斥量用來實現對讀者數量的互斥,由於該變量對於判斷是否寫是否重要
semaphore db=1,lock_in=1;//db是用來對資源進行互斥的訪問,可是lock_in是用來進行對當前的讀進行加鎖
int rc=0;//記錄當前讀者的數量,由於數量爲1的話說明這個進程開啓了讀,此時須要設置db
void read()
{
while(TRUE)
{
down(&lock_in); down(&mutex); rc+=1; if(rc==1) down(&db)
up(&lock_in); up(&mutex) read_data(); down(&mutex) rc-=1; if(rc==0) up(&db)//當前沒有讀者了 up(&mutex); use_data(); } } void writer() { while(TRUE) { down(&db) writer(); up(&db); } }

寫者:

semaphore  w_mutex;
int wc;
void writer()
{
while(TRUE)
{
down(&w_mutex);//與同時寫wc 的造成互斥
wc+=1;
up(&w_mutex);
if(wc==1)//>1的時候依據獲取了全部權
    down(&lock_in);//加鎖,禁止讀進程繼續添加到讀程序中 
down(&db)//db這下身兼多職,不只可以表明了讀寫互斥了,並且在寫裏面也可以互斥
write();
up(&db)//寫完成
down(&w_mutex);//與同時寫wc 的造成互斥
wc-=1;
up(&w_mutex);
if(wc==0)//=0時候,沒有寫了,交出全部權
    up(&lock_in);//容許讀進行進行添加任務

}

}

  

  lock_in 的具體意思是在writer進行寫等待的時候,全部的寫進程都會被阻塞在down(&db),此時lock_in加鎖,這也的後果是在寫進程以後的進程都會被阻塞在讀者的down(&lock_in),當前的讀者一旦完成讀的時候全部權db就會給寫者,此時寫者利用db進行同類互斥的寫,注意的是此時後加入的讀者還阻塞在down(&lock_in),修改db是無所謂的,在完成寫以後也就是wc=0的時候釋放阻塞在down(&lock_in)的進程。妙啊!

 4.2.3 公平競爭

  相似的思想能夠實現公平競爭,讀操做沒有變化,可是寫卻變化爲:

semaphore  w_mutex;
int wc;
void writer()
{
while(TRUE)
{
down(&lock_in);//加鎖,禁止讀進程繼續添加到讀程序中 ,同時能夠被阻塞 down(&w_mutex);//與同時寫wc 的造成互斥 wc+=1; up(&w_mutex);
up(&lock_in);//爲了將寫的其餘進程所有讀入,可以早釋放就早釋放。 down(&db)//db這下身兼多職,不只可以表明了讀寫互斥了,並且在寫裏面也可以互斥 write(); up(&db)//寫完成 down(&w_mutex);//與同時寫wc 的造成互斥 wc-=1; up(&w_mutex); } }

  誰先來誰就緊緊把握住lock_in阻止另外一種操做的註冊,很是公平,注意同類間的互斥操做又要求該臨界區儘量的短,以讓其它同類進程註冊。並且lock_in 這個鎖的做用時間長短直接決定了哪一個優先的操做

[1]、https://blog.csdn.net/sunny_ss12/article/details/47435687

相關文章
相關標籤/搜索