最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

你的新進(jìn)程是如何被內(nèi)核調(diào)度執(zhí)行到的?(下)

2022-11-19 14:38 作者:補(bǔ)給站Linux內(nèi)核  | 我要投稿

接上文你的新進(jìn)程是如何被內(nèi)核調(diào)度執(zhí)行到的?(上)

四、新進(jìn)程加入調(diào)度

進(jìn)程在 copy_process 創(chuàng)建完畢后,通過調(diào)用 wake_up_new_task 將新進(jìn)程加入到就緒隊(duì)列中,等待調(diào)度器調(diào)度。

今天我們可以來展開看看 wake_up_new_task 執(zhí)行時(shí)具體都發(fā)生了什么。新進(jìn)程是如何加入到 CPU 運(yùn)行隊(duì)列 (struct rq)中的,我們來展開詳細(xì)看看。

wake_up_new_task 主要做了兩件事,一是選擇一個(gè)合適 CPU,二是將進(jìn)程添加到所選的 CPU 的任務(wù)隊(duì)列中。


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




4.1 選擇合適的 CPU 運(yùn)行隊(duì)列

前面我們講到,每個(gè) CPU 核都有一個(gè)對應(yīng)的運(yùn)行隊(duì)列 runqueue (struct rq)。所以新進(jìn)程在加入調(diào)度前的第一件事就是選擇一個(gè)合適的運(yùn)行隊(duì)列。然后使用該任務(wù)隊(duì)列中,并等待調(diào)度。

要稍微注意的是,在 set_task_cpu(p, select_task_rq(p, SD_BALANCE_FORK, 0)) 這一行代碼中包含對兩個(gè)函數(shù)的調(diào)用。

  • select_task_rq 是選擇一個(gè)合適的 CPU(運(yùn)行隊(duì)列)

  • set_task_cpu 是使用選擇好的 CPU

在講選擇運(yùn)行隊(duì)列之前,我們先簡單回顧一下 CPU 里的緩存。


CPU 同一個(gè)物理核上的兩個(gè)邏輯核是共享一組 L1 和 L2 緩存的。整顆物理 CPU 上所有的核心共享同一組 L3。每一級 Cache 的訪問耗時(shí)都差別非常大,L1 大約是 1 ns 多一些,L2 大約是 2 ns 多,L3 大約 4 - 8 ns。而內(nèi)存耗時(shí)在最壞的隨機(jī) IO 情況下可以達(dá)到 30 多 ns。

了解了 CPU 的物理結(jié)構(gòu)以及各級緩存的性能差異,你就大概能弄明白選擇 CPU 的核心目的了。CPU 調(diào)度是在緩存性能和空閑核心兩個(gè)點(diǎn)之間做權(quán)衡。同等條件下會盡量優(yōu)先考慮緩存命中率,選擇同 L1/L2 的核,其次會選擇同一個(gè)物理 CPU 上的(共享 L3),最壞情況下去選擇另外一個(gè)物理 CPU 上的核心。

選擇運(yùn)行隊(duì)列 select_task_rq 這個(gè)函數(shù)有點(diǎn)復(fù)雜。但是理解了上面這個(gè)邏輯后,相信你理解起來就會容易很多。

在本文的第三節(jié)中我們提到了 fork 出來的新進(jìn)程的 sched_class 使用的是公平調(diào)度器 fair_sched_class,回憶一下這個(gè)結(jié)構(gòu)體的定義。

所以上面的 p->sched_class->select_task_rq 這一句實(shí)際是進(jìn)入到了 fair_sched_class 的 select_task_rq_fair 方法里,通過公平調(diào)度器實(shí)現(xiàn)的選擇任務(wù)隊(duì)列的來選擇的。

為了方便你理解,我把 select_task_rq_fair 源碼精簡處理后,只留下了關(guān)鍵邏輯。這個(gè)函數(shù)一開始就獲得了當(dāng)前 CPU(創(chuàng)建新進(jìn)程的進(jìn)程所使用的 CPU)和上一次運(yùn)行的 CPU(新進(jìn)程暫時(shí)還沒有)。接下來就是兩個(gè)關(guān)鍵邏輯:一是快速路徑選擇,二是慢速路徑選擇。

在快速路徑選擇中,主要的策略就是考慮共享 cache 且 idle 的 CPU。優(yōu)先選擇任務(wù)上一次運(yùn)行的CPU,其次是喚醒任務(wù)的 CPU??傊褪潜M量考慮 cache 性能。

如果快速路徑?jīng)]選到,那就進(jìn)入慢速路徑。首選選出負(fù)載最小的組(find_idlest_group),然后再從該組中選出最空閑的 CPU(find_idlest_cpu)。

當(dāng)進(jìn)入到慢速路徑以后,會導(dǎo)致進(jìn)程下一次執(zhí)行的時(shí)候跑的別的核、甚至是別的物理 CPU 上,這樣以前跑熱的 L1、L2、L3 就都失效了。用戶進(jìn)程過多地發(fā)生這種漂移會對性能造成影響。當(dāng)然內(nèi)核在極力地避免。如果你想強(qiáng)行干掉漂移,可以試試 taskset 命令。

至于 set_task_cpu 的邏輯比較簡單,主要就是把選到的 CPU 設(shè)置到新創(chuàng)建出來的進(jìn)程 task_struct 上。

4.2 將進(jìn)程添加到活動(dòng)進(jìn)程集合

在選擇完 CPU 后,下一步就是將新創(chuàng)建出來的進(jìn)程添加到該 CPU 對應(yīng)的運(yùn)行隊(duì)列 (struct rq) 中。

經(jīng)過 set_task_cpu 設(shè)置后,新進(jìn)程taskstruct 指針 p 上已經(jīng)記錄了下一次要使用的 CPU 號。調(diào)用 __task_rq_lock 函數(shù)的作用就是將新進(jìn)程 p 要使用的 CPU 的運(yùn)行隊(duì)列 struct rq 給找出來,并給它加個(gè)鎖防止沖突。

接著調(diào)用 activate_task 將新進(jìn)程添加到該 CPU 運(yùn)行隊(duì)列中去。


來查看其源碼。

回憶完全公平調(diào)度器 fair_sched_class 對象。

可見 p->sched_class->enqueue_task 實(shí)際調(diào)用的是 enqueue_task_fair。經(jīng)過 enqueue_task_fair => enqueue_entity ==> __enqueue_entity,最終插入到紅黑樹中等待調(diào)度。

五、調(diào)度時(shí)機(jī)

前面我們講述的過程全部是新進(jìn)程創(chuàng)建發(fā)生的事情,新進(jìn)程還沒有真正被調(diào)度。觸發(fā)調(diào)度器開始選擇進(jìn)程并上 CPU 開始運(yùn)行的時(shí)機(jī)有很多。我們就以咱們前面文章講過的同步阻塞時(shí)機(jī)為例。

在同步阻塞網(wǎng)絡(luò)編程模型下,如果 socket 上沒有收到數(shù)據(jù),或者收到不足夠多,則調(diào)用 sk_wait_data 把當(dāng)前進(jìn)程阻塞掉,讓出 CPU 并調(diào)度運(yùn)行隊(duì)列中的其它進(jìn)程進(jìn)行。

我們現(xiàn)在假設(shè)就有某一個(gè)進(jìn)程發(fā)生了這樣的阻塞。sk_wait_data 依次會調(diào)用 sk_wait_event、schedule_timeout,然后到達(dá)調(diào)度的核心函數(shù) schedule。我們來看看它的核心實(shí)現(xiàn) __schedule。

在這個(gè)函數(shù)中把當(dāng)前 CPU 的任務(wù)隊(duì)列取了出來,接著獲取下一個(gè)待運(yùn)行的任務(wù),再執(zhí)行上下文切換到新進(jìn)程上來運(yùn)行。接下來我們分兩個(gè)小節(jié)單獨(dú)來看下。

5.1 獲取下一個(gè)待執(zhí)行任務(wù)

是如何獲取下一個(gè)待執(zhí)行任務(wù)的呢?我們來看下 pick_next_task 的實(shí)現(xiàn)。

因?yàn)榇蟛糠侄际瞧胀ㄟM(jìn)程,所以大概率會執(zhí)行到 fair_sched_class.pick_next_task 函數(shù)中,也就是 pick_next_task_fair 函數(shù)中。該函數(shù)其實(shí)就是從當(dāng)前任務(wù)隊(duì)列的紅黑樹節(jié)點(diǎn)將運(yùn)行虛擬時(shí)間最小的節(jié)點(diǎn)(最左側(cè)的節(jié)點(diǎn))選出來而已。


這樣,下一個(gè)待運(yùn)行的進(jìn)程就被選出來了。

5.2 執(zhí)行上下文切換到新進(jìn)程上

選出來待運(yùn)行的新進(jìn)程以后,接著就需要執(zhí)行進(jìn)程上下文切換,把新進(jìn)程的運(yùn)行狀態(tài)給切換上來。

當(dāng)前進(jìn)程上下文切換完成的時(shí)候,新進(jìn)程終于可以得以運(yùn)行了!

六、總結(jié)

好了,我們把今天的文章的內(nèi)容總結(jié)一下。

一個(gè)進(jìn)程從 fork 創(chuàng)建出來到最后真正能獲得 CPU 并進(jìn)行運(yùn)行,中間有很多的內(nèi)核邏輯需要處理,我把它分成了這么幾個(gè)步驟供你更容易地理解。

第一,每個(gè) CPU 核都有一個(gè)運(yùn)行隊(duì)列。為了支持不同的調(diào)度需求,運(yùn)行隊(duì)列是由實(shí)時(shí)調(diào)度器、完全公平調(diào)取器等多種調(diào)度器組成。


第一,是進(jìn)程在 fork 的時(shí)候會選擇自己的調(diào)取器,用戶進(jìn)程一般都是用完全公平調(diào)度器(fair_sched_class)。 第二,進(jìn)程創(chuàng)建完前會綜合考慮緩存友好性以及空閑狀況,選擇一個(gè) CPU 運(yùn)行隊(duì)列出來,并將新進(jìn)程添加到該隊(duì)列中。


第三,內(nèi)核有很多的時(shí)機(jī)來觸發(fā)調(diào)度。我們文中舉了同步阻塞網(wǎng)絡(luò) IO 放棄 CPU 時(shí)調(diào)取新進(jìn)程運(yùn)行的例子。在放棄 CPU 前會從當(dāng)前 CPU 的運(yùn)行隊(duì)列獲取一個(gè)進(jìn)程出來,上下文切換后運(yùn)行之。


我們再回到開篇的問題:

問題一:進(jìn)程不主動(dòng)釋放 CPU 的話,每次調(diào)度最少能運(yùn)行多久? 在完全公平調(diào)度器中,出于減少頻繁切換進(jìn)程所帶來的成本考慮,一個(gè)進(jìn)程一旦被分配到 CPU 就會持續(xù)運(yùn)行相對較長的一段時(shí)間,避免頻繁的進(jìn)程上下文切換導(dǎo)致的性能損耗。這段時(shí)間的最小值由 sched_min_granularity_ns 這個(gè)內(nèi)核參數(shù)來控制,單位是 ns (納秒) 。例如下面這個(gè)配置的最短運(yùn)行時(shí)間是 10 ms。

當(dāng)然了,如果進(jìn)程因?yàn)榈却W(wǎng)絡(luò)、磁盤等資源時(shí)主動(dòng)放棄 CPU 那另算。

問題二:進(jìn)程的 nice 值代表的是優(yōu)先級嗎,高優(yōu)先級是否能搶占低優(yōu)先級的 CPU ? 在實(shí)時(shí)任務(wù)如 migration 內(nèi)核線程中,是按優(yōu)先級調(diào)度的。優(yōu)先級強(qiáng)調(diào)的是搶占,高優(yōu)先級比低優(yōu)先級有優(yōu)先獲得 CPU 的權(quán)利。

但是對于用戶進(jìn)程來講,一般都采用的完全公平調(diào)度器來進(jìn)行 CPU 資源的分配。在這種調(diào)取器中,其 nice 其實(shí)是一個(gè)權(quán)重的概念。權(quán)重高的進(jìn)程獲得的 CPU 比例會相對高一些。但不是實(shí)時(shí)搶占。


原文作者:開發(fā)內(nèi)功修煉


你的新進(jìn)程是如何被內(nèi)核調(diào)度執(zhí)行到的?(下)的評論 (共 條)

分享到微博請遵守國家法律
绵阳市| 游戏| 迁安市| 乌兰浩特市| 顺义区| 衢州市| 康马县| 迁安市| 南充市| 苍梧县| 隆化县| 务川| 东乌珠穆沁旗| 正蓝旗| 敦煌市| 樟树市| 兰溪市| 青冈县| 客服| 宁国市| 江都市| 阜城县| 泰兴市| 汾阳市| 临颍县| 绥宁县| 廉江市| 罗山县| 五台县| 扎赉特旗| 尼木县| 巧家县| 苍溪县| 安福县| 西充县| 江都市| 松滋市| 吉林省| 右玉县| 九龙坡区| 内黄县|