為什么會發生調度?
因為cpu是有限的,而操作系統上的進程很多,所以操作系統需要平衡各個進程的運行時間
比如說有的進程運行時間已經很長了,已經占用了cpu很長時間了,這個時候操作系統要公平
就會換下一個需要運行的進程。
舉個例子
公司只有一個飲水機用來接水,有很多人排隊,某個人接完了一杯水,又接下一杯水,一連接了好多杯水,這個時候公司的人事就要過來把這個人趕走
換下一個人接水,然而這個時候老板過來接水了,這個時候下一個人就是老板來接水,而不是后面排隊的人,因為什么呢?因為他是老板,就這么強悍
這里面的飲水機就是cpu
人事就是操作系統
等接水的一個個員工就是進程
老板就對應操作系統里的實時進程
普通員工就對應操作系統的普通進程
在操作系統里面實時進程的優先級比普通進程的優先級要高,
所以操作系統在選擇下個進程的時候會優先選擇實時進程隊列里面的進程
除非實時進程里面的進程沒有了,這個時候才輪到普通進程
操作系統就像人事一樣,需要公平的調度和分配資源,當然公司里面有很多老板啊,總經理啊,領導啊
這些人擁有特權,所有分配資源的時候要優先考慮這些老板領導們,這也很正常,誰讓人家是老板呢
下面來說說搶占式調度
什么情況下會發生搶占式調度呢?
最常見的現象是你這個進程運行時間太長了,是時候切換到另一個進程了
然而操作系統怎么去統計運行時間呢?
計算機有個時鍾的概念,每過一段時間,計算機會通知操作系統,告訴操作系統,又過去了一段時間,你去看看,當前運行的進程運行時間是不是過長了,這個時候操作系統就會去搞這個進程了。
在操作系統中對於每個進程有一個理想運行時間的變量,然后對應還有一個虛擬運行時間和實際運行時間和權重(優先級)。
這三者有什么關系呢?
虛擬運行時間 vruntime += 實際運行時間 delta_exec * NICE_0_LOAD/ 權重(優先級)
通過這個公式可以看出來,給高優先級的進程的虛擬運行時間算少了,給低優先級的進程的虛擬運行時間算多了,但是當操作系統選擇下一個進程的時候還是選擇虛擬運行時間最少的進程,所以說優先級在這里面就體現出來了。
當某個進程在運行的時候,這個進程的虛擬運行時間會增加,當進程不運行的時候,虛擬時間不增加
其實這里又會涉及到調度器的概念。
這里我們就只說針對普通進程的絕對公平調度策略
這種策略在選取下個進程的時候,是怎么選取的呢?
他是選取當前所有普通進程中運行時間最少的進程,這個應該很好理解吧,因為你在cpu上占用的時間最少,所以為了公平,就要選取你這個進程在cpu上運行。
在操作系統中維護了一個紅黑樹,紅黑樹就是一顆平衡二叉樹,也就是說紅黑樹上面掛了好多進程,最左邊的進程就是運行時間最少的進程,所有操作系統在,選取下一個進程就會選取這個紅黑樹上最左側的進程。
static void
check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{
unsigned long ideal_runtime, delta_exec;
struct sched_entity *se;
s64 delta;
ideal_runtime = sched_slice(cfs_rq, curr);
delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;
if (delta_exec > ideal_runtime) {
resched_curr(rq_of(cfs_rq));
return;
}
......
se = __pick_first_entity(cfs_rq);
delta = curr->vruntime - se->vruntime;
if (delta < 0)
return;
if (delta > ideal_runtime)
resched_curr(rq_of(cfs_rq));
}
上面代碼是Linux源碼
在操作系統中還有個時間的概念,就是在一個調度周期中,這個進程應該運行的實際時間(ideal_runtime)
sum_exec_runtime指進程總共執行的實際時間, prev_sum_exec_runtime指上次該進程被調度時已經占用的實際時間。
每次在調度一個新的進程時都會把它的se->prev_sum_exec_runtime=se->sum_exec_runtime,所以sum_exec_runtime-prev_sum_exec_runtime
就是這次調度占用實際時間。如果這個時間大於ideal_runtime,則應該被搶占了。
除了這個條件外,還會通過_pick_first_entity取出紅黑樹中最小的進程。如果當前進程的vruntime(虛擬運行時間)大於紅黑樹中最小的進程的vruntime,且差值大於ideal_time,也應該
被搶占了
當發現這個進程應該被搶占了,不能直接把他踢下去,而是在這個進程上打一個標簽TIF_NEED_RESCHED,標示這個進程可以被搶占了
還有一個可能發生搶占的場景,就是當一個休眠的進程被喚醒的時候
這個時候如果這個被喚醒的進程比當前運行的進程的優先級高,則也應該被搶占了,也是在當前運行的進程上打一個標簽TIF_NEED_RESCHED
搶占的時機
1.用戶態的搶占時機
當該進程進行系統調用從內核態返回到用戶態的時候,判斷如果該進程有TIF_NEED_RESCHED標簽,則進行搶占。
2.內核態的搶占時機
對內核態的執行中,被搶占的時機一般發生在preempt_enable()中。
preempt_disable()關閉搶占
在內核態的執行中,有的操作是不能被中斷的,所有在進行這些操作之前,總是先調用preempt_disable()關閉搶占,當再次打開的時候,也就是調用preempt_enable()的時候
就是一次內核態代碼被搶占的機會。
在內核態也會遇到中斷的情況,當中斷返回的時候,返回的仍然是內核態度。這個時候也是一個執行搶占的時機。
大家可以看看這張圖理解
