歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> 關於Linux >> 服務器架構設計4------進程調度

服務器架構設計4------進程調度

日期:2017/3/1 13:51:43   编辑:關於Linux

今天讓我們來一起了解一下linux cpu的進程調度,對於linux服務器,通常會碰到2個問題

1、實時性,有什麼辦法能確保某一個進程能優先運行、並且不受時間片的限制,只有等待它運行完了,其它進程才能運行?

2、多核cpu,有什麼辦法能夠自定義,綁定某些進程在某些cpu上?

那麼在探討這倆問題之前,先來了解一下linux進程調度基礎知識。

多任務系統分為2類。

非搶占式多任務:除非進程自己主動停止運行,否則它會一直執行;

搶占式多任務:有調度程序來決定什麼時候停止某一進程的運行,以便其它進程能夠得到執行機會。linux采用的是此種方式。

進程對於處理的使用上,也分為2類:

I/O消耗型:有大量的磁盤、網絡io操作,這種進程,其大部分時間都堵塞在io請求及其響應上;

處理器消耗型:這種程序大部分是算法很復雜,一個極端的例子就是while(1),死循環。

進程優先級:

高優先級的進程,先運行,並且其享用的時間片較長。低優先級進程則反之。

時間片:

過大、等待時間長,過小、進程切換頻繁。默認時間片20ms。

調度的公平性

在支持多進程的系統中,理想情況下,各個進程應該是根據其優先級公平地占有CPU。而不會出現“誰運氣好誰占得多”這樣的不可控的情況。

linux實現公平調度基本上是兩種思路:
1、給處於可執行狀態的進程分配時間片(按照優先級),用完時間片的進程被放到“過期隊列”中。等可執行狀態的進程都過期了,再重新分配時間片;
2、動態調整進程的優先級。隨著進程在CPU上運行,其優先級被不斷調低,以便其他優先級較低的進程得到運行機會;
後一種方式有更小的調度粒度,並且將“公平性”與“動態調整優先級”兩件事情合二為一,大大簡化了內核調度程序的代碼。因此,這種方式也成為內核調度程序的新寵。
強調一下,以上兩點都是僅針對普通進程的。而對於實時進程,內核既不能自作多情地去動態調整優先級,也沒有什麼公平性可言。

一個有趣的例子:

一個系統,2個進程,一個文字編輯、一個視頻編碼。前者是I/O消耗型,後者處理器消耗型。那麼處理器在對待這兩種進程是如何分配優先級和時間片的呢。

首先,文字編輯,其大部分時間都在I/O等待上,需要對用戶的請求及時響應,所以其優先級高,並且時間片長。當有用戶請求時,會中斷視頻編碼的運行。當需要等待I/O響應時,會及時交出時間片,給視頻編碼用。

相反,視頻編碼,優先級低,時間片短。

===========================================================================

好,基本知識介紹完了,下面來回答開篇提的2個問題。

linux兩種實時調度策略:

SCHED_NORMAL:普通調度策略,平時我們所使用,基於時間片的搶占式調度策略。

SCHED_FIFO:先入先出調度,不使用時間片,一旦一個SCHED_FIFO級進程處於可執行狀態,就會一直執行下去,直到它自己受阻塞或顯式地釋放cpu為止。FIFO比NORMAL優先級高,只有較高優先級的SCHED_FIFO或SCHED_RR任務才能搶占SCHED_FIFO任務。

SCHED_RR:和SCHED_FIFO類似,但是使用時間片,其優先級比SCHED_NORMAL高。當SCHED_RR時間片耗盡,相同優先級的其它實時進程會被輪流調度,注意是相同優先級的實時進程。換句話說,當其時間片耗盡,只有相同優先級的SCHED_FIFO和SCHED_RR可以被cpu調度,而低優先級的SCHED_NORMAL是不會被輪流到的。當然高優先級的實時進程,可以搶占。

實時優先級范圍0~99,99是最高優先級。可以通過函數sched_get_priority_max獲取。

總結:實時調度,可以使用SCHED_FIFO和SCHED_RR,區別是前者沒有時間片,知道其運行完畢或受阻塞,後者有時間片,時間片耗盡,cpu可以交給同優先級的實時任務使用。

說了一大堆,到底如何設置實時調度策略,函數有哪些呢?

nice() //設置進程的nice值
sched_setscheduler()//設置進程的調度策略
sched_getscheduler()//獲取進程的調度策略
sched_setparam()//設置進程的實時優先級
sched_getparam()//獲取進程的實時優先級
sched_get_priority_max()//獲取實時優先級最大值
sched_get_priority_min()//獲取實時優先級最小值
sched_rr_get_interval()//獲取進程時間片
sched_setaffinity()//設置進程處理器的親和力
sched_getaffinity()//獲取進程處理器的親和力
sched_yield()//暫時讓出處理器

設置實時調度策略SCHED_FIFO例子:

int
rtsched_set_my_realtime_priority (int32_t prio) {
    struct sched_param schp;
    if (sched_getparam(0, &schp) < 0) {
        return(-1);
    }

    schp.sched_priority = prio;
    if (sched_setscheduler(0, SCHED_FIFO, &schp) < 0) {
        return(-1);
    }

    return(1);
}


綁定指定cpu的例子:

int
rtsched_setaffinity_by_name(int32_t cpuid)
{
    cpu_set_t mask;
    CPU_ZERO(&mask);
    CPU_SET(cpuid, &mask);
    sched_setaffinity(0, sizeof(cpu_set_t), &mask);

    return 1;
}
這是一個綁定cpu的例子,系統默認進程可以在任何一個cpu上運行,但為了保證某些進程的實時性,把它綁定在某個空閒cpu上運行也是很有必要的。

放棄cpu

好了,綁定cpu,設定優先級,都保證了某個進程的實時性,那麼如果我們想暫時放棄其實時性,讓其讓出cpu,讓別的進程運行一會,有什麼辦法呢?

可以調用函數sched_yield(),其將進程從活動隊列移到過期隊列中,交出其占用的cpu,需要注意的是,對於實時進程,不是將其放倒過期隊列中,而是放到優先級隊列的最後面。而不會放到過期隊列中去。

需要注意的地方:

1、最好優先級,千萬別隨便設置,一旦設置其他進程就沒得玩了,最高99,設個98就已經很高了,作者曾經試驗過,一旦設置99,連ssh都連不上了,囧。。。。。。。;

2、對於cpu的綁定和優先級的設定,是可以針對線程的,O(∩_∩)O~;

3、高優先級,則代表不去釋放cpu,假設有這樣一種情況,pthread1、pthread2都綁定在cpu1上,並且都是實時同優先級的線程。1獲取到spin_lock,然後io阻塞交出cpu給2,恰巧2和1共享同一資源,也要去spin_lock同一資源,好吧,想想看,會是什麼結局,2會一直spin_lock,占用cpu,而1又獲取不到cpu,這2位就在這僵著,誰也無法繼續執行。囧。。。。。。。。。

Copyright © Linux教程網 All Rights Reserved