Linux Linux下的程序排程

2021-09-26 06:08:12 字數 4160 閱讀 8342

在多程序的作業系統中,程序排程是乙個全域性性、關鍵性的問題,他對系統的總體設計、系統的實現和功能設定以及各個方面的效能都有著決定性的作用。本部落格筆者就對程序的排程這一知識點進行總結,不過為了簡單起見,我們先從linux核心2.4中排程介紹起,將原理說清楚後介紹更高階的linux2.6核心排程演算法。

乙個好的排程演算法說明白點其實就是能夠更合理的分配系統資源,所以排程演算法應該具備以下特點:

時間片輪轉演算法

時間片就是分配給程序執行的一段時間,而此演算法中就是讓程序排成乙個佇列,處於佇列首部的程序能夠被cpu執行。執行乙個時間片後系統發出訊號通知排程程式終止此程序的執行,並將它送到佇列的尾部進行等待。時間片的大小為幾毫秒到幾百毫秒不等。此演算法能保證在某乙個特定時間內所有的程序都能被執行,這個時間能被人接受。

優先權排程演算法

時間片輪轉演算法能保證每乙個程序都能得到合理的cpu執行時間,但是某種常見下為了讓緊迫性程序得到優先處理,引入了優先權排程演算法。

多級反饋佇列排程

這是一種折中的辦法。其本質將時間片輪轉演算法和搶占式優先權排程演算法結合。也就是讓優先順序高的程序先執行給定的時間片,相同優先權的程序輪流執行給定的時間片。

據筆者了解,當前bsd下使用的就是多級反饋佇列的排程演算法。

實時排程

實時排程也叫實時系統中的排程。實時系統就是對外部時間有求必應、盡快響應。在實時系統中存在若干個實時程序和任務,他們用來反應或控制某個外部事件。一般使用搶占式的排程方式。

linux的排程程式是乙個名叫schedule的函式,這個函式我們在之前的程序切換中已經提到過了,這個函式被呼叫的頻率特別高。這個函式用來決定是否進行程序的切換,如果要切換那麼應該切換到哪個程序。我們來大體列出linux下需要排程的時機

排程程式執行時,要選擇最值得投入執行的程式。那麼選擇這些最值得投入程式時的依據是什麼呢?在test_strut結構體中有這麼幾個和程序排程相關的域。

網域名稱描述

need_resched

決定是否呼叫schedule函式

counter

剩餘的時鐘節拍數,每次時鐘中斷來到時,這個值就會減一

nice

程序的基本優先順序,她覺得counter的初值,這個值範圍為-20~19,這個值越底程序優先順序越高

policy

sched_fifo:先入先出的實時程序 sched_rr:時間片輪轉實時程序 sched_other:普通分時程序

rt_priority

實時程序的優先順序

值得強調的是counter時鐘節拍其實就時間片。每乙個節拍如果為10ms,那麼如果節拍為2的話就代表這個程序的時間片長度為20ms

linux中使用乙個名叫goodness的函式來計算當前程序的優先級別:

static

inline

intgoodness

(struct task_struct * p, int this_cpu,\

struct mm_struct *this_mm)

//將counter的值賦給weight,這就給了程序乙個大概的權值,

//counter中的值表示程序在乙個時間片內,剩下要執行的時間.

weight = p->counter;

if(!weight)

//weight==0,表示該程序的時間片已經用完,則直接轉到標號out

goto out;

#ifdef __smp__

/*在smp情況下,如果程序將要執行的cpu與程序上次執行的cpu是一樣的,

則最有利,因此,假如程序上次執行的cpu與當前cpu一致的話,

權值加上proc_change_penalty,這個巨集定義為20。*/

if(p->processor == this_cpu)

weight +

= proc_change_penalty;

#endif

if(p->mm == this_mm)

//程序p與當前執行程序,是同乙個程序的不同執行緒,

//或者是共享位址空間的不同程序,優先選擇,權值加1

weight +=1;

weight +

= p->priority; //權值加上程序的優先順序

out:

return weight; //返回值作為程序排程的唯一依據,誰的權值大,就排程誰執行

}

在這之前我們談到程序排程的演算法還是基於核心2.4版本的,但是隨著處理器的增多,我們之前的演算法會存在這樣的問題。

多處理器問題:多個處理器上的程序都放在乙個就緒佇列中,這就使得這個佇列成為了臨界資源,各個處理器因為等待進入就緒佇列而降低了系統效率。

核心態不可搶占問題:只要有乙個程序進入了核心態,即使有乙個非常緊迫的任務到來,他也只能等著。只有那個程序從核心返回到使用者態時,緊迫任務才能占有處理器。

o(1)演算法的runqueue

針對上面的問題,2.6排程演算法給出了這樣的乙個結構:

針對多處理器的問題,給每個cpu都設定乙個就緒佇列。針對單就緒佇列的問題,設定倆個佇列組:active佇列組和時間片到期expried佇列組。每個佇列組中以優先順序再進行分類,相同優先順序的程序為乙個佇列,最多有140個優先順序,也就對應140個佇列

如上圖,沒有毫完時間片的程序位於活動佇列陣列中,耗完時間片的程序存放到expried佇列組中,從而節省空轉的時間。當一輪排程結束後,活動隊列為空,交換倆個佇列組的指標進行下一輪的排程。上圖中佇列的資料結構定義如下:

struct runqueue

prio_array定義為:

struct prio_array

觀察這個資料結構時我們發現了乙個位圖,還記的我們演算法的名字??o(1)演算法的runqueue。從結構上來看,乙個cpu上的就緒佇列最多可達280個。如何快速的選出要執行的程序成了系統效能的乙個關鍵性因素。而這個位圖設定為1時表示程序以排程就緒,從最高優先順序開始,位圖第乙個被置為1的佇列將被取出排程。

而為了提高程序的響應時間,o(1)演算法同樣使用時間片輪轉演算法的動態調整思想,每次時鐘中斷程序的時間片就減一,當counter減為0如果是互動式或者實時程序就重置時間片並再次加入active組中,如果是普通程序就加入到expired組中等待下一次的排程。不過如果實時程序一直被排程就會發生其他程序排程的飢餓現象,所以即便是實時程序在排程一定的cpu時間後也會被插入到expired組中。

樓梯演算法

為了解決動態調整的問題,大量的複雜**被加入到2.6的排程模組中,這就導致**的可維護度極大的降低。而在2023年,大佬們又提出了乙個新的方法——樓梯演算法。

樓梯演算法並沒有完全拋棄o(1)演算法的框架,而是保留了active陣列,丟棄了expired陣列。現在乙個程序用完時間片後並不是被移到expired組中,而是被加入到低一優先順序佇列中。這裡注意只是將任務插入低一優先順序的佇列中,任務本身的優先順序並沒有發生變化。當時間片再次被用完時,就加入更低一級的佇列中。

當任務到達最低一級樓梯時,如果時間片再次用完,他會回到初始化優先順序下一任務佇列中。如果優先順序為3,那麼下次就加入到優先順序為4的佇列。不過將該任務的時間片變為原來的兩倍。如果再一次的到達最低一級佇列時,下一次就再放到優先順序為5的佇列中,不過這次的時間片與最初的時間片相同。

以上對於樓梯演算法的描述是普通程序的排程演算法,實時程序還是採用原來的排程策略,即fifo或者輪詢演算法。可以看出樓梯演算法使用不一樣的思路大大優化了**。並且事實證明樓梯演算法比o(1)演算法更好。

部落格中並未給出排程函式schedule,函式本身不結合上下文難以理解,所以有興趣的同學可以自己看看源**。本部落格重點理解最重要的o(1)演算法和樓梯演算法,如果有什麼不懂小夥伴們可以儘管指出。

Linux linux下的程序狀態

程序狀態有 就緒 執行 阻塞 linux下程序的狀態 執行態 r 就緒以及正在執行,執行狀態的程序才會被作業系統排程在cpu上執行 可中斷休眠態 s 當前的阻塞能夠被中斷喚醒的休眠狀態 不可中斷休眠態 d 當前的阻塞不會被中斷喚醒,等待條件滿足自動喚醒 暫停態 t 停止執行,什麼都不做 殭屍態 z ...

linux linux程序指令

1.linux有哪些程序 linux下的主要程序狀態有 2.1 r task running 可執行狀態 通過將程序的task struct結構放到cpu的可執行佇列中,使程序變成r態。只有處在該狀態的程序才有可能被程序排程器選中在cpu上執行 2.2 s task interruptible 可中...

linux linux程序記憶體分析

在產品的開發中,通過對當前系統消耗記憶體總量的統計,可以對產品所需記憶體總量進行精確的評估,從而選擇合適的記憶體晶元與大小,降低產品的成本。在遇到記憶體洩露類問題時,經常會對此束手無策,本文通過對proc下程序相關的檔案進行分析,精確評估系統消耗記憶體的大小,還可以對記憶體洩露類問題的解決提供一種定...