Linux學習-進程管理與調度(四)-負載均衡與實時性

一、多核負載均衡

負載均衡這個概念是針對多核CPU而言的,希望達到的狀態(tài)是各個核的調度情況盡量保持一致,不要使某一個核太忙也不能使某一個核太輕松,核與核之間相對均勻分配任務。而每個獨立的核上的調度策略依然是使用SCHED_FIFO算法、SCHED_RR算法與SCHED_NORMAL算法。

不同進程如何做到負載均衡:

  • RT進程

N個優(yōu)先級最高的進程分不到N個不同的核,使用pull_rt_task與push_rt_task來達到負載均衡的效果。RT進程的話,實際上強調的是實時性而不是負載均衡。

  • 普通進程

周期性負載均衡:所有的進程周期性的被各個核調度達到多個CPU的負載均衡。

IDLE時負載均衡:一旦跑了0號進程,說明整個系統(tǒng)處于一種低功耗的狀態(tài),這種狀態(tài)下整個系統(tǒng)只有0號進程會跑,其他進程都在休眠。這里的負載均衡說的是只要其他核還在忙,當前核就會想辦法去幫忙,而不是進入IDLE狀態(tài)。

fork和exec時負載均衡:當創(chuàng)建一個新的進程或者替換了一個新進程,就會把這個新的task_struct推給一個最空閑的核去調度。

二、負載均衡限制

這里講的是如何打破常規(guī)的負責均衡,主要有兩個策略:

1)cpu task affinity

affinity的意思是親和的意思,讓task_struct對某一個或若干個CPU親和。也就是讓task_struct只在某幾個核上跑,不去其他核上跑。

如何實現(xiàn)CPU task affinity?

  • 代碼API實現(xiàn):
int pthread_attr_setaffinity_np(pthread_attr_t *, size_t, const cpu_set_t *);1
int pthread_attr_getaffinity_np(pthread_attr_t *, size_t, cpu_set_t *);
int sched_setaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);
int sched_getaffinity(pid_t pid, unsigned int cpusetsize, cpu_set_t *mask);

設置掩碼來保證某一個線程對某幾個核親和,比如下方的0x6(110),就是設置線程只能在核2與核1上運行。

  • taskset工具實現(xiàn):
taskset -a -p 01 23421

注:這里01是掩碼(0001)表示0核,02(0010)表示1核,03(0011)表示0核或者1核,以此類推。 23421是進程pid。-a 是所有線程。

2)cgroup

調度思想:進程分群,群與群之間CFS調度,群內部再CFS調度。這種分層思想能保證1000個線程的A程序與10個線程的B程序能相對公平地得到調度。如果僅僅只在線程間CFS,那么A程序得到調度的概率遠大于B程序。

這里如何限制呢?

編譯two-loops.c, gcc two-loops.c -pthread,運行三份

$ ./a.out &
$ ./a.out &
$ ./a.out &

用top觀察CPU利用率,大概各自66%。

限制方法1:換cgroup

創(chuàng)建A,B兩個cgroup:

/sys/fs/cgroup/cpu$ sudo mkdir A
/sys/fs/cgroup/cpu$ sudo mkdir B

把3個a.out中的2個加到A,1個加到B:

/sys/fs/cgroup/cpu/A$ sudo sh -c ‘echo 3407 > cgroup.procs’
/sys/fs/cgroup/cpu/A$ sudo sh -c ‘echo 3413 > cgroup.procs’
/sys/fs/cgroup/cpu/B$ sudo sh -c ‘echo 3410 > cgroup.procs’

這時發(fā)現(xiàn)3個a.out的CPU利用率大概是50%, 50%, 100%。

限制方法2:調整cgroup的權重
/sys/fs/cgroup/cpu/A$ sudo sh -c ‘echo 2048 > cpu.shares’

然后B的權重保持1024,那么3個a.out的CPU利用率大概又回到了各占60%多的樣子。

限制方法3:限制一個cgroup在一個周期內最多跑多久。

cpu.cfs_quota_us:限制一個cgroup組在period時間周期內跑多長時間。
cpu.cfs_period_us:時間周期。

/sys/fs/cgroup/cpu/A$ cat cpu.cfs_period_us
100000   
/sys/fs/cgroup/cpu/A$ sudo sh -c ‘echo 20000 > cpu.cfs_quota_us’

A這個group里面cfs進程100000us周期內,最多可以跑20000us,那么相當于CPU利用率為20%。若超過cpu.cfs_period_us,按N核*100 %算。

三、實時性

硬實時:任務從喚醒到調度,時間不超過某一個預定的截止期限,具有可預期性。
軟實時:相比硬實時,被調度的時間允許超過那個截止期限,無法做到精確可控。

Linux就屬于軟實時系統(tǒng)。

但是kernel隨著版本的迭代,越發(fā)地往硬實時靠了。

Linux為什么不是硬實時的?

因為總結起來有3個不可搶占區(qū)間:

  • 中斷狀態(tài):當系統(tǒng)中有中斷,CPU不能再調度任何其他進程,就算RT進程來了也一樣得等著中斷結束后的一瞬間才能搶占CPU。而且在中斷中,不能再進行中斷,也就是說,中斷必須結束才能干其他事。中斷是必須要被處理的。

  • 軟中斷狀態(tài):軟中斷中可以被中斷。但是軟中斷中如果喚醒一個RT進程,此RT進程也不會被調度。

  • 進程處于spin_lock(自旋鎖)狀態(tài):自旋鎖是發(fā)生在兩個核之間的。當某一個核如CPU0上的進程獲取spin_lock后,該核的調度器將被關閉。如果另一個核如CPU1的進程task_struct1此時想要獲取spin_lock,那么task_struct1將自旋。自旋的意思就是不停的來查看是否spin_lock被解鎖,不停的占用CPU直到可以獲取spin_lock為止。所以進程如果處于spin_lock,那么其他任何進程不會被調度。

實例說明:

運行分析:

  • T0時刻:假設有一個系統(tǒng)調用陷入到內核中。此時在跑的是一個普通進程(Normal task)。
  • T1時刻:該Normal task獲取了一個spin_lock。
  • T2時刻:突然來了一個中斷IRQ1,則系統(tǒng)執(zhí)行中斷處理函數(shù)IRQ1 handle,在中斷處理函數(shù)中又調用軟中斷(Soft IRQ)。
  • T3時刻:在軟中斷中喚醒了一個RT進程。此時由于系統(tǒng)處于軟中斷狀態(tài),所以RT進程無法搶占CPU(紅色虛線部分為無法搶占CPU)。
  • T4時刻:又來了一個中斷IRQ2(說明軟中斷中可以中斷),然后系統(tǒng)執(zhí)行中斷處理函數(shù)IRQ2 handler,然后執(zhí)行軟中斷處理函數(shù)。
  • T5時刻:中斷與軟中斷執(zhí)行完畢。但是由于此時Normal task還處于spin_lock狀態(tài),所以之前被喚醒的RT進程還是依然無法占用CPU。
  • T6時刻:Normal task釋放了spin_lock的一瞬間,RT進程搶占了CPU。當RT進程執(zhí)行完,才會把CPU還給最開始還沒有執(zhí)行完的Normal task。Normal task執(zhí)行完后,退出內核的系統(tǒng)調用。

RT在T3時刻被喚醒,因為中斷、軟中斷、自旋鎖的影響,直到T6才得到調度,因此T3-T6這個階段是不可控的,根據硬實時的概念知,Linux系統(tǒng)不是硬實時的。

參考:
宋寶華Linux的進程、線程以及調度
《 Linux內核設計與實現(xiàn)》

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
禁止轉載,如需轉載請通過簡信或評論聯(lián)系作者。

相關閱讀更多精彩內容

友情鏈接更多精彩內容