Linux Scheduler之rt選核流程
前言
在Linux中,有些線程需要被公平調(diào)度,保證每個線程不會長時間的調(diào)度不到,這就是我們熟知的CFS調(diào)度類(sched class),但是也有一些關(guān)鍵線程(比如一些顯示刷幀的支撐線程),我們需要保證線程能夠及時被調(diào)度到,針對普通負(fù)載較輕的場景,線程的調(diào)度及時性都能得到保證。但是為了滿足人們的日常使用需求,操作系統(tǒng)后臺駐留任務(wù)越來越多(這種現(xiàn)象在Android設(shè)備上表現(xiàn)尤為嚴(yán)重),而系統(tǒng)的CPU始終只有一個,即使這個CPU有8個核甚至更多,CPU也有可能被塞滿task,為了保證一些重要task的及時運(yùn)行,這里就有了實(shí)時進(jìn)程的概念。
Linux中,系統(tǒng)是通過優(yōu)先級來區(qū)分非實(shí)時線程和實(shí)時線程的,Linux將線程優(yōu)先級分為140個等級,從0~139,這個值越小其優(yōu)先級越高,實(shí)時線程也叫rt(real time)線程,其優(yōu)先級范圍為[0,99],非實(shí)時線程為[100,139]。非實(shí)時線程也叫cfs線程,他的default優(yōu)先級為120,通過nice值轉(zhuǎn)化為最終的線程優(yōu)先級。Nice值的取值范圍為-20~19,可以通過ps命令查看NI列:

本文主要講述rt線程的選核流程。
rt選核流程介紹
每個task在被wakup喚醒時候都會從try_to_wake_up開始執(zhí)行,這里會為task選擇合適的CPU運(yùn)行,其核心邏輯位于select_task_rq函數(shù)里面,它會根據(jù)task的sched_class確定調(diào)用的具體函數(shù),而task的sched_class初始化則是在系統(tǒng)更改或者設(shè)置task的priority時,根據(jù)task的priority進(jìn)行設(shè)置:

如果一個線程為rt,它的sched_class則為rt_sched_class,結(jié)構(gòu)體初始化定義如下:

那么rt線程對應(yīng)調(diào)度類的選核函數(shù)為select_task_rq_rt,其基本的執(zhí)行邏輯如下圖1:

rt選核流程比較簡單,其核心邏輯位于find_lowest_rq函數(shù)中,此函數(shù)主要用于尋找符合當(dāng)前rt線程運(yùn)行的cpu,其核心邏輯如下圖2:

cpupri_find_fitness負(fù)責(zé)從所有系統(tǒng)中所有的符合task運(yùn)行條件的cpu找出來,并更新到lowest_mask里面,然后find_lowest_rq再從最終的lowest_mask里面選擇合適的CPU,選擇邏輯:
如果lowest_mask里面包含task的prev cpu,則直接選擇prev cpu。
選擇lowest_mask在task的sched domain里面的第一個cpu。
如果前面都沒找到CPU,則判斷l(xiāng)owest_mask里面是否包含了wake cpu,如果包含則直接返回wake cpu
最后如果都沒找到,lowest又不為空,則從lowest_mask里面找一個隨機(jī)的cpu返回。
下面來看下cpupri_find_fitness如何找到合適的所有適合task運(yùn)行的cpu,主要分為3個部分:

1.首先對task優(yōu)先級進(jìn)行一個轉(zhuǎn)化,將系統(tǒng)中的task分為0~102個等級,優(yōu)先級由低到高,可以分為invalid,idle,normal和rt四類。

其中invalid優(yōu)先級為0,idle優(yōu)先級為1,所有的cfs線程占用一類,優(yōu)先級為2,rt則每個優(yōu)先級占用一個等級。
2.因為rt線程是可以搶占的,for循環(huán)從最低優(yōu)先級開始遍歷,這里的優(yōu)先級為已經(jīng)轉(zhuǎn)化為103個級別的優(yōu)先級狀態(tài),其選核邏輯一般為首先選擇idle的cpu運(yùn)行,其次是搶占有cfs task的cpu運(yùn)行,最后才會考慮取搶占其他低優(yōu)先級rt task的cpu,通過__cpupri_find查找各個優(yōu)先級在cpu上的運(yùn)行狀態(tài),找到可以使用的cpu。cpupri_vec結(jié)構(gòu)體有兩個成員,count用來存儲各個優(yōu)先級task在哪些CPU上屬于最高優(yōu)先級task,mask則用來存儲當(dāng)前優(yōu)先級所在CPU上是最高優(yōu)先級的的cpumask。例如當(dāng)前系統(tǒng)中cpu0~5上沒有全是cfs task在運(yùn)行,那么normal task的count值為6,mask為0x3f。

__cpupri_find的基本邏輯:
判斷當(dāng)前優(yōu)先級在哪些CPU上是最高優(yōu)先級,如果沒有的話,說明當(dāng)前系統(tǒng)要么沒有此優(yōu)先級task,要么是當(dāng)前優(yōu)先級task并非系統(tǒng)中各個CPU的最高優(yōu)先級。
當(dāng)前優(yōu)先級task在有cpu是處于最高優(yōu)先級,從這些cpu里面去除掉task not allowed cpu,再去除掉被isolation的cpu,最后如果lowest_mask還有CPU,則將lowest_mask作為后面選核的基礎(chǔ)。
經(jīng)過__cpupri_find找到lowest_mask后,再從lowest_mask里面過濾掉capacity無法滿足當(dāng)前task的cpu,最后剩下的就是可以用來運(yùn)行當(dāng)前task的cpumask,如果沒有剩余,則說明當(dāng)前優(yōu)先級的task所運(yùn)行的cpu沒有滿足條件的,此時需要進(jìn)入下一個循環(huán),找到更高優(yōu)先級的task運(yùn)行的cpu。
3.如果此次循環(huán)沒有找到合適的cpu,會再進(jìn)行一次循環(huán),嘗試找到合適的CPU。
至此,rt選核的整體流程介紹完畢。
結(jié)語
本文主要從代碼的角度講述了Linux rt選核的主要流程,旨在讓讀者對于rt線程及其選核邏輯有一個初步的認(rèn)識,利用rt線程的優(yōu)點(diǎn),可以解決當(dāng)前系統(tǒng)中因為調(diào)度延遲引起的一些性能問題。
rt線程選核相對cfs線程而言要簡單一些,他不會考慮energy等因素,但系統(tǒng)中過多的rt task可能會帶來一定的功耗影響,同時由于rt主要是為了解決重要task的調(diào)度延遲問題,如果系統(tǒng)中過多的rt線程可能導(dǎo)致一些重負(fù)載場景可能所有CPU都是rt task,引發(fā)rt線程的調(diào)度延遲,從而導(dǎo)致更嚴(yán)重的性能問題,所以也不宜在系統(tǒng)中設(shè)置過多的rt task。
引文:
[1] https://mirrors.edge.kernel.org/pub/linux/
[2] 深入理解LINUX內(nèi)核(第三版)(美)博韋,西斯特 著,陳莉君,馮銳,牛欣源 譯
[3] http://www.wowotech.net/process_management/process-priority.html Linux調(diào)度器-進(jìn)程優(yōu)先級

關(guān)注內(nèi)核工匠微信
Linux內(nèi)核黑科技|?技術(shù)文章 |?精選教程