1、O(1)調度算法算法
1.1:優先級數組數組
O(1)算法的:一個核心數據結構即爲prio_array結構體。數據結構
該結構體中有一個用來表示進程動態優先級的數組queue,它包括了每一種優先級進程所造成的鏈表。post
1 |
#define MAX_USER_RT_PRIO 100 |
2 |
#define MAX_RT_PRIO MAX_USER_RT_PRIO |
3 |
#define MAX_PRIO (MAX_RT_PRIO + 40) |
4 |
typedef struct prio_array prio_array_t; |
5 |
struct prio_array { |
6 |
unsigned int nr_active; |
7 |
unsigned long bitmap[BITMAP_SIZE]; |
8 |
struct list_head queue[MAX_PRIO]; |
9 |
}; |
由於進程優先級的最大值爲139,所以MAX_PRIO的最大值取140(詳細的是,普通進程使用100到139的優先級。實時進程使用0到99的優先級)。所以,queue數組中包括140個可執行狀態的進程鏈表,每一條優先級鏈表上的進程都具備一樣的優先級,而不一樣進程鏈表上的進程都擁有不一樣的優先級。spa
除此以外,prio_array結構中還包含一個優先級位圖bitmap。該位圖使用一個位(bit)來表明一個優先級,而140個優先級最少需要5個32位來表示,所以BITMAP_SIZE的值取5。起初。該位圖中的所有位都被置0,當某個優先級的進程處於可執行狀態時。該優先級所相應的位就被置1。操作系統
所以,O(1)算法中查找系統最高的優先級就轉化成查找優先級位圖中第一個被置1的位。指針
與2.4內核中依次比較每個進程的優先級不一樣,由於進程優先級個數是定值,所以查找最佳優先級的時間恆定。它不會像曾經的方法那樣受可運行進程數量的影響。code
假設肯定了優先級。那麼選取下一個進程就簡單了,僅僅需在queue數組中相應的鏈表上選取一個進程就能夠。orm
在操做系統原理課上咱們知道,當處於執行態的進程用完時間片後就會處於就緒態。此時調度程序再從就緒態的進程中選取一個做爲即將要執行的進程。blog
而在詳細Linux內核中,就緒態和執行態統一稱爲可執行態(TASK_RUNNING)。
對於系統內處於可執行狀態的進程,咱們可以分爲三類。首先是正處於執行狀態的那個進程;其次,有一部分處於可執行狀態的進程則還沒實用完他們的時間片。他們等待被執行;剩下的進程已經用完了本身的時間片,在其它進程沒實用完它們的時間片以前,他們不能再被執行。
據此,咱們將進程分爲兩類,活動進程,那些還沒實用完時間片的進程。過時進程。那些已經用完時間片的進程。所以,調度程序的工做就是在活動進程集合中選取一個最佳優先級的進程,假設該進程時間片剛好用完,就將該進程放入過時進程集合中。
在可執行隊列結構中,arrays數組的兩個元素分別用來表示剛纔所述的活動進程集合和過時進程集合,active和expired兩個指針分別直接指向這兩個集合。
關於可執行隊列和兩個優先級數組的關係可參考如下的圖:
正如上面分析的那樣,可執行隊列結構和優先級數組結構使得Q(1)調度算法在有限的時間內就可以完畢,它不依賴系統內可執行進程的數量。
Linux2.4版本號的內核調度算法理解起來簡單:在每次進程切換時。內核依次掃描就緒隊列上的每一個進程,計算每一個進程的優先級,再選擇出優先級最高的進程來執行;雖然這個算法理解簡單。但是它花費在選擇優先級最高進程上的時間卻不容忽視。
系統中可執行的進程越多。花費的時間就越大。時間複雜度爲O(n)。
僞代碼例如如下:
1 |
for (系統中的每個進程) { |
2 |
又一次計算時間片; |
3 |
又一次計算優先級; |
4 |
} |
而2.6內核所採用的O(1)算法則很是好的攻克了這個問題,該算法可以在恆定的時間內爲每個進程又一次分配好時間片,而且在恆定的時間內可以選取一個最高優先級的進程,重要的是這兩個過程都與系統中可執行的進程數無關,這也正是該算法取名爲O(1)的緣故。
O(1)算法採用過時進程數組和活躍進程數組解決以往調度算法所帶來的O(n)複雜度問題。過時數組中的進程都已經用完了時間片。而活躍數組的進程還擁有時間片。當一個進程用完本身的時間片後,它就被移動到過時進程數組中。同一時候這個過時進程在被移動以前就已經計算好了新的時間片。
可以看到O(1)調度算法是採用分散計算時間片的方法,並不像以往算法中集中爲所有可執行進程又一次計算時間片。
當活躍進程數組中沒有不論什麼進程時。說明此時所有可執行的進程都用完了本身的時間片。那麼此時僅僅需要交換一下兩個數組就能夠將過時進程切換爲活躍進程,進而繼續被調度程序所調度。兩個數組之間的切換事實上就是指針之間的交換,所以花費的時間是恆定的。如下的代碼說明了兩個數組之間的交換:
1 |
struct prop_array *array = rq->active; |
2 |
if (array->nr_active != 0) { |
3 |
rq->active = rq->expired; |
4 |
rq->expired = array; |
5 |
} |
經過分散計算時間片、交換過時和活躍兩個進程集合的方法可以使得O(1)算法在恆定的時間內爲每個進程又一次計算好時間片。
進程調度的本質就是在當前可執行的進程集合中選擇一個最佳的進程,這個最佳則是以進程的動態優先級爲選取標準的。
不管是過時進程集合仍是活躍進程集合,都將每個優先級的進程組成一個鏈表,所以每個集合就有140個不一樣優先級的進程鏈表。同一時候。兩個集合中還採用優先級位圖來標記每個優先級鏈表中是否存在進程。
調度程序在選取最高優先級的進程時。首先利用優先級位圖從高到低找到第一個被設置的位,該位相應着一條進程鏈表。這個鏈表中的進程是當前系統所有可執行進程中優先級最高的。在該優先級鏈表中選取頭一個進程,它擁有最高的優先級。即爲調度程序當即要執行的進程。
上述進程的選取過程可用下述代碼描寫敘述:
01 |
struct task_struct *prev, *next; |
02 |
struct list_head *queue; |
03 |
struct prio_array *array; |
04 |
int idx; |
05 |
06 |
prev = current; |
07 |
array = rq->active; |
08 |
idx = sehed_find_first_bit(array->bitmap); |
09 |
queue = array->queue + idx; |
10 |
next = list_entry(queue->next, struct task_struct, run_list); |
11 |
if (prev != next) |
12 |
context_switch(); |
sehed_find_first_bit()用於在位圖中高速查找第一個被設置的位。假設prev和next不是一個進程。那麼此時進程切換就開始運行。
經過上述的內容可以發現。在恆定的時間又一次分配時間片和選擇一個最佳進程是Q(1)算法的核心。
2、全然公平(CFS)調度算法
一個調度算法中最重要的兩點就是調度哪一個進程以及該被調度進程的執行時間是多少,如下咱們分別來討論
2.1:調度哪一個進程
O(1)算法是依據進程的優先級來選擇調度進程的,而CFS是依據進程的虛擬執行時間來進行調度的,固然,該虛擬執行時間也會受到優先級的影響。但不全是。如下看看什麼是虛擬執行時間。以及怎樣依據它來選擇下一個調度進程。
CFS算法的初衷就是讓所有進程同一時候執行在一個CPU上,好比兩個進程都需要執行10ms的時間,則CFS算法下,連個進程同一時候執行在CPU上,且時間爲20ms,而不是每個進程分別執行10ms。但是這僅僅是一種理想的執行方式,CFS爲了近似這樣的執行算法,就提出了虛擬執行時間(vruntime)的概念。
vruntime記錄了一個可執行進程到當前時刻爲止執行的總時間(需要以進程總數n進行歸一化,並且依據進程的優先級進行加權)。依據vruntime的定義可以知道,vruntime越大,說明該進程執行的越久,因此被調度的可能性就越小。因此咱們的調度算法就是每次選擇vruntime值最小的進程進行調度。內核中使用紅黑樹可以方便的獲得vruntime值最小的進程。
至於每個進程怎樣更新本身的vruntime?內核中是依照例如如下方式來更新的:vruntime += delta* NICE_0_LOAD/ se.weight;當中:
NICE_0_LOAD是個定值。及系統默認的進程的權值;se,weight是當前進程的權重(優先級越高。權重越大);
delta是當前進程執行的時間;咱們可以得出這麼個關係:vruntime與delta成正比,即當前執行時間越長vruntime增加越快
vruntime與se.weight成反比,即權重越大vunruntime增加越慢。簡單來講,一個進程的優先級越高,而且該進程執行的時間越少,則該進程的vruntime就越小,該進程被調度的可能性就越高。
2.2:調度進程的執行時間
現在知道了怎樣調度進程了,但是當該進程執行時,它的執行時間是多少呢?
CFS的執行時間是有當前系統中所有可調度進程的優先級的比重來肯定的,假如現在進程中有三個可調度進程A、B、C,它們的優先級分別爲5,10,15,則它們的時間片分別爲5/30,10/30,15/30。
而不是由本身的時間片計算得來的,這種話,優先級爲1,2的兩個進程與優先級爲50,100的兩個進程分的時間片是一樣的。
簡單來講,CFS採用的所有進程優先級的比重來計算每個進程的時間片的,是相對的而不是絕對的。
這樣就從以上兩個方面來分析了CFS進程調度算法