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