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

本文主要講述rt線程的選核流程。
【文章福利】小編推薦自己的Linux內核技術交流群:【891587639】整理了一些個人覺得比較好的學習書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)?


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

如果一個線程為rt,它的sched_class則為rt_sched_class,結構體初始化定義如下:

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

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

cpupri_find_fitness負責從所有系統(tǒng)中所有的符合task運行條件的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里面找一個隨機的cpu返回。
下面來看下cpupri_find_fitness如何找到合適的所有適合task運行的cpu,主要分為3個部分:

1.首先對task優(yōu)先級進行一個轉化,將系統(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)轉化為103個級別的優(yōu)先級狀態(tài),其選核邏輯一般為首先選擇idle的cpu運行,其次是搶占有cfs task的cpu運行,最后才會考慮取搶占其他低優(yōu)先級rt task的cpu,通過__cpupri_find查找各個優(yōu)先級在cpu上的運行狀態(tài),找到可以使用的cpu。cpupri_vec結構體有兩個成員,count用來存儲各個優(yōu)先級task在哪些CPU上屬于最高優(yōu)先級task,mask則用來存儲當前優(yōu)先級所在CPU上是最高優(yōu)先級的的cpumask。例如當前系統(tǒng)中cpu0~5上沒有全是cfs task在運行,那么normal task的count值為6,mask為0x3f。

__cpupri_find的基本邏輯:
判斷當前優(yōu)先級在哪些CPU上是最高優(yōu)先級,如果沒有的話,說明當前系統(tǒng)要么沒有此優(yōu)先級task,要么是當前優(yōu)先級task并非系統(tǒng)中各個CPU的最高優(yōu)先級。
當前優(yōu)先級task在有cpu是處于最高優(yōu)先級,從這些cpu里面去除掉task not allowed cpu,再去除掉被isolation的cpu,最后如果lowest_mask還有CPU,則將lowest_mask作為后面選核的基礎。
經(jīng)過__cpupri_find找到lowest_mask后,再從lowest_mask里面過濾掉capacity無法滿足當前task的cpu,最后剩下的就是可以用來運行當前task的cpumask,如果沒有剩余,則說明當前優(yōu)先級的task所運行的cpu沒有滿足條件的,此時需要進入下一個循環(huán),找到更高優(yōu)先級的task運行的cpu。
3.如果此次循環(huán)沒有找到合適的cpu,會再進行一次循環(huán),嘗試找到合適的CPU。
至此,rt選核的整體流程介紹完畢。
結語
本文主要從代碼的角度講述了Linux rt選核的主要流程,旨在讓讀者對于rt線程及其選核邏輯有一個初步的認識,利用rt線程的優(yōu)點,可以解決當前系統(tǒng)中因為調度延遲引起的一些性能問題。
rt線程選核相對cfs線程而言要簡單一些,他不會考慮energy等因素,但系統(tǒng)中過多的rt task可能會帶來一定的功耗影響,同時由于rt主要是為了解決重要task的調度延遲問題,如果系統(tǒng)中過多的rt線程可能導致一些重負載場景可能所有CPU都是rt task,引發(fā)rt線程的調度延遲,從而導致更嚴重的性能問題,所以也不宜在系統(tǒng)中設置過多的rt task。
原文作者:內核工匠
