:從概念到實戰(zhàn)】04:調(diào)度算法對決:SCHED_FIFO/SCHED_RR與普通調(diào)度器)
【實時Linux核心技術(shù):從概念到實戰(zhàn)】04:調(diào)度算法對決:SCHED_FIFO/SCHED_RR與普通調(diào)度器摘要:在汽車電子、工業(yè)控制、機器人等場景里,一個控制回路的延遲抖動就可能引發(fā)災(zāi)難。Linux內(nèi)核默認(rèn)的完全公平調(diào)度器(CFS)追求整體吞吐量上“誰也不吃虧”的公平,而實時調(diào)度策略SCHED_FIFO和SCHED_RR追求的則是確定性的響應(yīng)。它們與普通調(diào)度器共存于同一內(nèi)核,但遵循的規(guī)則可以說完全不在一個維度。本文從優(yōu)先級模型、搶占機制、優(yōu)先級反轉(zhuǎn)、內(nèi)核節(jié)流保護一路講到實戰(zhàn)配置,結(jié)合Mermaid調(diào)度時序圖、C代碼實例和chrt/cyclictest等工具演示,把三種調(diào)度策略的差別和協(xié)作方式掰開揉碎講清楚。讀完你不僅能理解為什么實時任務(wù)可以隨時“霸占”CPU,還會掌握如何設(shè)計優(yōu)先級拓?fù)洹⑴渲脙?yōu)先級繼承、利用實時節(jié)流保住系統(tǒng)底線,真正拿到一套可落地的設(shè)計方法論。文中少不了踩坑經(jīng)驗——我就吃過優(yōu)先級繼承沒開的虧,導(dǎo)致整個控制系統(tǒng)周期抖動超過100毫秒。優(yōu)質(zhì)專欄歡迎訂閱!【OpenClaw從入門到精通】【DeepSeek深度應(yīng)用】【Python高階開發(fā):AI自動化與數(shù)據(jù)工程實戰(zhàn)】【YOLOv11工業(yè)級實戰(zhàn)】【機器視覺:C# + HALCON】【軟件設(shè)計師·軟考50講通關(guān)|從零基礎(chǔ)到工程師職稱】【人工智能之深度學(xué)習(xí)】【AI 賦能:Python 人工智能應(yīng)用實戰(zhàn)】【數(shù)字孿生與仿真技術(shù)實戰(zhàn)指南】【YOLOv8/v9/v10 實戰(zhàn)與工業(yè)部署】【C#工業(yè)上位機高級應(yīng)用:高并發(fā)通信+性能優(yōu)化】【Java生產(chǎn)級避坑指南:高并發(fā)+性能調(diào)優(yōu)終極實戰(zhàn)】【Coze搞錢實戰(zhàn):零代碼打造吸金AI助手】【YOLO26核心改進+場景落地實戰(zhàn)寶典】【OpenClaw企業(yè)級智能體實戰(zhàn)】文章目錄【實時Linux核心技術(shù):從概念到實戰(zhàn)】04:調(diào)度算法對決:SCHED_FIFO/SCHED_RR與普通調(diào)度器1. 調(diào)度器的兩條路線:公平與確定性2. 實時優(yōu)先級模型:1-99的數(shù)字游戲3. SCHED_FIFO 與 SCHED_RR 的調(diào)度規(guī)則3.1 SCHED_FIFO:先到先得,絕不裝友善3.2 SCHED_RR:時間片輪轉(zhuǎn),公平一小撮3.3 調(diào)度時序圖示例4. 實時任務(wù)與CFS任務(wù)的共存機制5. 優(yōu)先級反轉(zhuǎn)與優(yōu)先級繼承協(xié)議5.1 什么是優(yōu)先級反轉(zhuǎn)5.2 普通互斥鎖的不足5.3 rtmutex與優(yōu)先級繼承5.4 用戶態(tài)互斥鎖啟用PIP6. 實時節(jié)流:防餓死機制6.1 問題:實時任務(wù)可能讓系統(tǒng)“卡死”6.2 內(nèi)核節(jié)流參數(shù)6.3 如何調(diào)整6.4 調(diào)參實踐與監(jiān)控6.5 陷阱:多核場景下的節(jié)流是 per-CPU7. 實踐:配置調(diào)度策略(從命令到代碼)7.1 用 chrt 工具操作7.2 使用 sched_setscheduler 系統(tǒng)調(diào)用(C代碼)7.3 查看實時調(diào)度統(tǒng)計8. 典型模式:高優(yōu)先級采集 + 低優(yōu)先級處理8.1 設(shè)計思路8.2 偽代碼擴展8.3 實測效果9. 設(shè)計原則與最佳實踐9.1 優(yōu)先級拓?fù)洌喊殃P(guān)鍵任務(wù)往高處放9.2 盡量避免在實時線程里做可能阻塞的事9.3 善用監(jiān)控和調(diào)試工具9.4 理解內(nèi)核搶占配置9.5 常見問題與解決10. 總結(jié)與展望1. 調(diào)度器的兩條路線:公平與確定性內(nèi)核的設(shè)計者弄出了一個多調(diào)度類(Scheduling Class)的框架,每個任務(wù)在創(chuàng)建時就會被標(biāo)記屬于某一個調(diào)度類。調(diào)度器在選擇下一個要跑的任務(wù)時,會按照固定的優(yōu)先級順序:stop → dl → rt → fair → idle。這個順序是死的,任務(wù)只能在自己所屬的那一層跟同級“內(nèi)卷”,跨層的話,低層的根本就沒有機會。我們來畫一張調(diào)度類層次圖,幫你建立直觀印象:是否是否是否是否調(diào)度器 pick_next_taskstop調(diào)度類最高優(yōu)先級,系統(tǒng)操作有任務(wù)?執(zhí)行任務(wù)dl調(diào)度類Deadline調(diào)度有任務(wù)?rt調(diào)度類SCHED_FIFO / SCHED_RR有任務(wù)?fair調(diào)度類CFS, SCHED_OTHER有任務(wù)?idle調(diào)度類最低優(yōu)先級空轉(zhuǎn)從這張圖你就看出來了,rt類哪怕只有一個優(yōu)先級為1的任務(wù)處于可運行狀態(tài),fair類的所有CFS任務(wù)都只能在旁邊干瞪眼。這種硬性的等級制,就是實時調(diào)度的“霸道”根本。當(dāng)然,霸道的另一面就是餓死普通任務(wù),后面我們會看到內(nèi)核用什么手段來兜底。CFS在干什么?普通調(diào)度器(fair類)沿用的是CFS算法,它的目標(biāo)是為每個任務(wù)公平分配CPU時間。它不關(guān)心誰先來、誰更急,只關(guān)心在宏觀時間尺度上每個任務(wù)的虛擬運行時間盡量拉平。這么做在桌面和服務(wù)器上是非常牛的,但對于控制周期為500微秒的運動控制器,CFS可能會把該周期任務(wù)調(diào)度延遲個幾十毫秒——因為此時可能正有數(shù)據(jù)庫的日志刷盤線程在和它公平競爭。所以說,實時任務(wù)沒法接受這種“平均意義上的公平”,它們要的是“我就得在這個時間窗內(nèi)上CPU”。2. 實時優(yōu)先級模型:1-99的數(shù)字游戲?qū)崟r優(yōu)先級是1到99之間的整數(shù),數(shù)字越大優(yōu)先級越高。普通任務(wù)的nice值范圍是-20到19,但這個優(yōu)先級只在fair類內(nèi)部作計算虛擬時間的權(quán)重用。內(nèi)核在管理任務(wù)時,用一個統(tǒng)一的prio字段表示調(diào)度優(yōu)先級,類型如下:任務(wù)類型prio范圍說明實時任務(wù) (rt_priority 0)0 ~ 99但實際上prio會被調(diào)整為MAX_RT_PRIO - 1 - rt_priority,使得最終數(shù)值越小優(yōu)先級越高,好讓調(diào)度器按順序快速選出普通任務(wù)100 ~ 139對應(yīng)nice值-20~19的映射,同樣數(shù)值越小越優(yōu)先即便你看top時,核心只分“rt優(yōu)先級”和“nice值”兩類,但這背后的映射機制決定了只要rt_priority非零,該任務(wù)的prio肯定在99以內(nèi),從而在調(diào)度類比較中直接干翻所有CFS任務(wù)。你可以用ps -eL -o pid,tid,cls,rtprio,comm查看到線程的實時優(yōu)先級。比如我手頭一個采集進程的輸出:PID TID CLS RTPRIO COMMAND 1123 1123 RR 50 data_collector 1123 1124 FF 80 data_proc_rt 1123 1125 TS - data_loggerCLS列:FF就是SCHED_FIFO,RR是SCHED_RR,TS表示SCHED_OTHER(基于時間片,也就是CFS)。RTPRIO顯示的是實時優(yōu)先級數(shù)值。設(shè)計的時候,你需要根據(jù)任務(wù)的截止期和重要性,給出一套合理的優(yōu)先級拓?fù)洌ㄎ覀儠诘?節(jié)專門聊這個)。3. SCHED_FIFO 與 SCHED_RR 的調(diào)度規(guī)則3.1 SCHED_FIFO:先到先得,絕不裝友善SCHED_FIFO是一種“跑起來就不松手”的調(diào)度方式。它的規(guī)則簡單粗暴:一個SCHED_FIFO任務(wù)一旦變?yōu)榭蛇\行狀態(tài),如果它優(yōu)先級大于當(dāng)前正執(zhí)行的任務(wù),就直接把對方踢飛,自己上CPU。上CPU之后,只有以下條件之一發(fā)生才會被換下:主動阻塞:比如調(diào)用sleep、等待信號量、讀I/O等;主動放棄CPU:調(diào)用sched_yield();被更高優(yōu)先級的實時任務(wù)搶占;進程終止。沒有時間片這個概念。也就是說,如果存在兩個同是優(yōu)先級60的SCHED_FIFO任務(wù),先跑的那個會一直霸占CPU不放,后面的那個只能等前者阻塞或主動放棄——這種“靜坐”多數(shù)情況下是設(shè)計缺陷,除非你刻意用阻塞來做同步。3.2 SCHED_RR:時間片輪轉(zhuǎn),公平一小撮SCHED_RR在SCHED_FIFO基礎(chǔ)上增加了同優(yōu)先級時間片輪轉(zhuǎn)。內(nèi)核維護每個實時優(yōu)先級的運行隊列,一個SCHED_RR任務(wù)默認(rèn)每次能連續(xù)跑的時間由/proc/sys/kernel/sched_rr_timeslice_ms決定,一般是100毫秒。時間片用完后,任務(wù)被甩到同優(yōu)先級隊列的末尾,換下一個同優(yōu)先級RR任務(wù)執(zhí)行。還是拿之前那個場景,如果你把兩個優(yōu)先級60的任務(wù)都設(shè)為SCHED_RR,那它們就會交替運行,每個最多跑100ms。這種輪轉(zhuǎn)讓同優(yōu)先級的實時任務(wù)不至于相互餓死,但注意,這種“公平”僅限實時同類內(nèi)部,CFS任務(wù)依然完全得不到CPU。3.3 調(diào)度時序圖示例為了給你一個直觀感受,我們畫一張多任務(wù)混合的時序圖。假設(shè)系統(tǒng)中有:Task A:SCHED_OTHER(CFS),普通優(yōu)先級,始終可運行。Task B:SCHED_FIFO,優(yōu)先級50。Task C:SCHED_RR,優(yōu)先級50。Task D:SCHED_FIFO,優(yōu)先級80。用Mermaid畫個簡化時序(注意這里描述的不是內(nèi)核級別精確的逐微秒切換,而是宏觀行為):DC