【Linux进程、线程、任务调度】四多核下负载均衡 中断负载均衡,RPS软中断负载均衡 cgroups与CPU资源分群分配 Linux为什么不是硬实时 preempt-rt对Linux实时性的改造
學習交流加
- 個人qq:
1126137994 - 個人微信:
liu1126137994 - 學習交流資源分享qq群:
962535112
上一篇文章(點擊鏈接:點擊鏈接閱讀上一篇文章)講了:
- CPU/IO消耗型進程
- 吞吐率 vs. 響應
- SCHED_FIFO算法 與 SCHED_RR算法
- SCHED_NORMAL算法 和 CFS算法
- nice與renice
- chrt
本篇文章接著上一篇文章講解以下內容:
- 多核下負載均衡
- 中斷負載均衡,RPS軟中斷負載均衡
- cgroups與CPU資源分群分配
- Linux為什么不是硬實時
- preempt-rt對Linux實時性的改造
文章目錄
- 1、多核下負載均衡
- 2、CPU task affinity
- 3、IRQ affinity
- 4、進程間的分群(cgroup)
- 5、 Hard realtime - 可預期性
- 6、PREEMPT_RT補丁
- 7、總結
1、多核下負載均衡
我們知道現在的CPU都是多核的。我們可以認為在同一時刻,同一個核中只能有一個進程(task_struct,調度單位但是task_struct)在運行。但是多核的時候,在同一個時刻,不同的核中的進程是可以同時運行的。
假設你電腦有四個核,現在每個核都在跑一個線程。各個核上都是獨立的使用SCHED_FIFO算法、SCHED_RR算法與CFS完全公平調度算法去調度自己核上的task_struct,但是為了能夠使整個系統的負載能夠達到均衡(各個核的調度情況盡量保持一致,不要使某一個核太忙也不能使某一個核太輕松),某一個核也有可能會把自己的task_struct給另一個核,讓另一個核來調度它。各個核都是以勞動為樂,會接收更多的任務,核與核之間進行pull與push操作將各自的task_struct給其他核或者拿其他核的task_struct來調度。這樣的話,整個系統就會達到一種負載均衡的效果。我們稱之為多核下的負載均衡。
那么不同的進程如何做到負載均衡呢?
- RT進程
N個優先級最高的進程分不到N個不同的核,使用pull_rt_task與push_rt_task來達到負載均衡的效果。RT進程的話,實際上強調的是實時性而不是負載均衡。
- 普通進程
分為:
- 實驗
two-loops.c
#include <stdio.h> #include <pthread.h> #include <sys/types.h>void *thread_fun(void *param) {printf("thread pid:%d, tid:%lu\n", getpid(), pthread_self());while (1) ;return NULL; }int main(void) {pthread_t tid1, tid2;int ret;printf("main pid:%d, tid:%lu\n", getpid(), pthread_self());ret = pthread_create(&tid1, NULL, thread_fun, NULL);if (ret == -1) {perror("cannot create new thread");return 1;}ret = pthread_create(&tid2, NULL, thread_fun, NULL);if (ret == -1) {perror("cannot create new thread");return 1;}if (pthread_join(tid1, NULL) != 0) {perror("call pthread_join function fail");return 1;}if (pthread_join(tid2, NULL) != 0) {perror("call pthread_join function fail");return 1;}return 0; }編譯運行:
$ gcc two-loops.c -pthread $ time ./a.out- 結果分析
由于我的虛擬機中Linux是兩個核的,在兩個核中分別跑的時間加起來,大概等于我們用戶態的時間。這說明兩個線程被分配到兩個核中分別跑的。
2、CPU task affinity
affinity的意思是親和,實際上我們這里是指,讓task_struct對某一個或若干個CPU親和。也就是讓task_struct只在某幾個核上跑,不去其他核上跑。這樣實際上破壞了多核的負載均衡。
如何實現CPU task affinity?
設置掩碼來保證某一個線程對某幾個核親和,比如下方的0x6(110),就是設置線程只能在核2與核1上運行
比如:
$ taskset -a -p 01 19999-a:進程中的所有線程,01掩碼,19999進程pid
- 實驗
編譯上述two-loops.c, gcc two-loops.c -pthread,運行一份
$ ./a.out &top查看CPU占用率:
把它的所有線程affinity設置為01, 02, 03后分辨來看看CPU利用率
$ taskset -a -p 02 進程PID $ taskset -a -p 01 進程PID $ taskset -a -p 03 進程PID- 前兩次設置后,a.out CPU利用率應該接近100%,最后一次接近200%
3、IRQ affinity
中斷也可以達到負載均衡。
假設有四個網卡,當網卡收到數據,會觸發中斷,將四個網卡隊列的中斷均分給四個CPU
- my ethernet
/proc/irq/74/smp_affinity 000001
/proc/irq/75/smp_affinity 000002
/proc/irq/76/smp_affinity 000004
/proc/irq/77/smp_affinity 000008
以上四個網卡的中斷全部均分給了四個CPU。
當然中斷也可以像進程一樣讓其affinity某個進程,比如向下面這樣,可以讓01號中斷分配給某個CPU,讓其affinity該CPU。
分配IRQ到某個CPU
[root@boss ~]# echo 01 > /proc/irq/145/smp_affinity [root@boss ~]# cat /proc/irq/145/smp_affinity 00000001有一種情況比較特殊:假設一個CPU0上有一個中斷IRQ,該中斷處理函數中可能會調用軟中斷(soft_irq)處理函數。那么這個軟中斷處理函數又會占用該CPU0。那么該CPU0就會處于非常忙的狀態,達不到負載均衡。如何使軟中斷去其他核執行?
使用RPS解決多核間的softIRQ scaling 。
RPS可以將包處理(中斷里面的處理,其實就是軟中斷)負載均衡到多個CPU
例如:
[root@machine1 ~]# echo fffe > /sys/class/net/eth1/queues/rx-0/rps_cpus 將中斷分配給0~15的核,這樣可以使所有核共同處理中斷以及中斷內部的軟中斷,處理TCP/IP包的解析過程4、進程間的分群(cgroup)
進程間的分群:假設有一個編譯Android系統的服務器,兩個人A與B同時要使用該服務器編譯程序,A編譯程序創建了1000個線程,B編譯程序創建了32個線程,那么如果按正常的CFS調度的話,A的程序會獲得1000/1032的CPU時間,B的程序會獲得32/1032的CPU時間,這樣的話就會導致編譯B可能會花與A相同的時間才能將程序編譯完,這樣就顯得很對B不公平(想想我B本身可能是一個小程序,卻要編譯半天,多難受啊)。Linux為了解決類似的這種問題,采用了進程間的分群思想:讓A的線程放在一個群中,B的線程放在一個群中,給A群與B群采用CFS調度各個群,然后再在A群與B群內部采用CFS調度群內的進程。這樣的話,就顯得公平一些。不會說編譯一個小程序花費太多時間。
實際上分群使用的是樹結構,上圖可以清晰的理解。
- 實驗
編譯two-loops.c, gcc two-loops.c -pthread,運行三份
$ ./a.out & $ ./a.out & $ ./a.out &用top觀察CPU利用率,大概各自66%。
-
創建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/A$ cd …
/sys/fs/cgroup/cpu$ cd B/
/sys/fs/cgroup/cpu/B$ sudo sh -c ‘echo 3410 > cgroup.procs’ -
這次發現3個a.out的CPU利用率大概是50%, 50%, 100%。
5、 Hard realtime - 可預期性
硬實時:可預期性。當一個線程被喚醒,直到這個線程被調度的這個時間段,不超過某一個預定的截止期限。稱為硬實時。如果該線程被喚醒后,被調度的時間允許超過那個截止期限,那么就不是硬實時。Linux系統不是硬實時。
以上圖我們可以看到。Linux系統并不是硬實時系統,也就是說對于一個進程,什么時間之前(注意我們不能說在什么時候能夠被調度到,因為我們無法確定一個進程什么時候能夠被調度到)能夠被調度到,我們并不知道。那么Linux為什么不是硬實時?
首先我們需要理解Linux系統中,有四類區間:
當Linux跑起來后,CPU的時間都花在上述四類區間上。
- 中斷狀態:
當系統中有中斷,CPU不能再調度任何其他進程,就算RT進程來了也一樣得等著中斷結束后的一瞬間才能搶占CPU。而且,在中斷中,不能再進行中斷,也就是說,中斷必須結束才能干其他事。中斷是必須要被處理的。
- 軟中斷
軟中斷中可以被中斷。但是軟中斷中如果喚醒一個RT進程,此RT進程也不會被調度。
- 進程處于spin_lock(自旋鎖)
自旋鎖是發生在兩個核之間的。當某一個核如CPU0上的進程獲取spin_lock后,該核的調度器將被關閉。如果另一個核如CPU1的進程task_struct1此時想要獲取spin_lock,那么task_struct1將自旋。自旋的意思就是不停的來查看是否spin_lock被解鎖,不停的占用CPU直到可以獲取spin_lock為止。
所以進程如果處于spin_lock,那么其他任何進程不會被調度。
- 進程處于THREAD_RUNNING態
當進程處于THREAD_RUNNING態,它就是可調度的,只有在這種狀態下,CPU才支持搶占。也就是說在這種狀態,加入CPU正在運行一個普通進程,此時如果某一個RT進程被喚醒,那么該RT進程就會去搶占CPU。
上述四類區間:如果可搶占的RT進程被喚醒在前三類區間,那么該RT進程,必須等待這三類區間的事件完成結束的一瞬間搶占,否則RT進程也不會被CPU調度。如果在第四類區間上喚醒一個RT進程,則該RT進程立即搶占CPU。
理解了以上四類區間,就很容易理解Linux為什么不是硬實時了,看下圖:
分析:
上圖橫軸為時間軸。T0,T1,T2…為某一時刻。
- 系統運行分析:
在T0時刻,假設有一個系統調用陷入到內核中。此時在跑的是一個普通進程(Normal task),在T1時刻,該Normal task獲取了一個spin_lock。
到了T2時刻,突然來了一個中斷IRQ1,則系統執行中斷處理函數IRQ1 handle人(),再中斷處理函數中又調用軟中斷(Soft IRQ),在軟中斷中的T3時刻,喚醒了一個RT進程。此時由于系統處于軟中斷狀態,所以RT進程無法搶占CPU(紅色虛線部分為無法搶占CPU)。在T4(軟中斷執行期間)時刻,又來了一個中斷IRQ2(說明軟中斷中可以中斷),然后系統執行中斷處理函數IRQ2 handler(),然后執行軟中斷處理函數。到T5時刻中斷與軟中斷執行完畢。但是由于此時Normal task還處于spin_lock狀態,所以之前被喚醒的RT進程還是依然無法占用CPU。直到T6時刻,Normal task釋放了spin_lock的一瞬間,RT進程搶占了CPU。當RT進程執行完,才會把CPU還給最開始還沒有執行完的Normal task。Normal task執行完后,退出內核的系統調用。
- 結果分析:
從以上分析可以看出,從T3時刻RT進程被喚醒,到T6時刻,RT進程開始執行,這段時間,我們是無法預測的,我們無法給出一個有限的上限值來度量T6-T3的值。因為在這期間,有可能會來各種中斷,有可能進程會一直處于spin_lock狀態不放。所以,我們無法確定T6-T3的時間段。所以根據硬實時的概念知,Linux系統,不是硬實時。
6、PREEMPT_RT補丁
可以對Linux系統打實時補丁來增加Linux 的實時性。
比如PREEMPT_RT補丁,主要的原理如下:
- spinlock遷移為可調度的mutex,同時報了raw_spinlock_t
- 實現優先級繼承協議
- 中斷線程化
- 軟中斷線程化
將spin_lock與中斷,軟中斷,都改造成第四類區間的可調度區間,就可以實現Linux系統的硬實時性。比如當中斷線程化,當產生中斷時,不執行中斷處理程序,直接返回。只有很小的區間是不可搶占的。
- 以上四種方法原理,后期會詳細研究,這里不再贅述。
7、總結
本文主要掌握:
- 多核下負載均衡
- 中斷負載均衡,RPS軟中斷負載均衡
- cgroups與CPU資源分群分配
- Linux為什么不是硬實時
- preempt-rt對Linux實時性的改造
探討學習加:
qq:1126137994
微信:liu1126137994
總結
以上是生活随笔為你收集整理的【Linux进程、线程、任务调度】四多核下负载均衡 中断负载均衡,RPS软中断负载均衡 cgroups与CPU资源分群分配 Linux为什么不是硬实时 preempt-rt对Linux实时性的改造的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php运行代码运行退出为0,php –
- 下一篇: centos7静默搭建oracle11g