核實時系統(tǒng):Linux 4.9環(huán)境搭建與延遲排查實戰(zhàn))
1. 先說結(jié)論這套雙內(nèi)核組合解決的是最壞情況延遲剛接手產(chǎn)線項目那會兒看到 xenomai 3.0.5/3.0.9 linux 4.9.38/4.9.90 這套組合第一反應也是都什么年代了還在用 4.9 內(nèi)核等我把 EtherCAT 主站跑起來、把伺服周期從 1ms 壓到 500us才明白這套老組合能一直在工控設備上存活是有理由的。這篇文章就是把環(huán)境搭建到穩(wěn)定運行的配置細節(jié)以及過程中踩過的一堆 BUG 原原本本記下來給后面接手類似項目的人省點時間。Xenomai 的 Cobalt 模式本質(zhì)是在 Linux 內(nèi)核里插入一條 IRQ pipeline中斷流水線讓實時任務跑在一個更霸道的執(zhí)行域里。普通 Linux 任務照常使用各種生態(tài)實時任務走專用通道兩者共享 CPU 但優(yōu)先級完全不對等。它真正的價值不是平均延遲而是最壞情況延遲。PREEMPT_RT 方案能把平均延遲做到幾十微秒但 worst case 偶爾給你來一下幾百微秒運動控制里這一下可能就是一次急停報警。Xenomai 要做的就是把 max 值釘死在個位數(shù)的微秒量級這是雙內(nèi)核方案到今天仍被工控行業(yè)繼續(xù)采用的核心原因。這篇文章適合兩類人一類是在 x86_64 工控機或 ARM 板子上做運動控制、數(shù)據(jù)采集需要硬實時但還沒碰過 Xenomai 的開發(fā)者另一類是已經(jīng)能編譯內(nèi)核、但被各種延遲毛刺折騰得睡不著覺的老手。我會把版本搭配、打補丁、內(nèi)核配置、用戶態(tài)編譯運行、以及故障排查的思路全部攤開講每個 BUG 都會說明現(xiàn)象、根因和最終的處理手段盡量讓你能照著抄作業(yè)。1.1 Cobalt 雙內(nèi)核到底怎么個工作法理解 Xenomai 不用想得太玄。你可以把 Linux 內(nèi)核想象成一棟寫字樓普通任務就是樓里的普通上班族按電梯排隊上樓。Xenomai 做的是在這棟樓里加了一條 VIP 專用電梯通道所有外部中斷先經(jīng)過這條通道的保安亭實時任務要處理的中斷直接走 VIP 電梯剩下 Linux 不關心的中斷才放行給普通電梯。這個保安亭就是 I-pipe/IRQ pipeline實時任務的運行環(huán)境就是 Cobalt 核。所以 Xenomai 的實時任務和 Linux 任務并不是兩個操作系統(tǒng)各跑各的而是共享同一個 CPU 資源池、由中斷優(yōu)先級強行分出了先后。Cobalt 域的實時線程永遠先于 Linux 域的進程調(diào)度Linux 域密集計算再厲害也干擾不到實時任務的關鍵路徑。這個設計帶來的代價是要打內(nèi)核補丁而且補丁版本必須和內(nèi)核版本嚴格對齊這正是本文后半部分大量 BUG 的源頭。1.2 適合誰、不適合誰這套方案適合交互頻繁、對確定性要求苛刻的場景EtherCAT 主站、CNC 插補、多軸協(xié)調(diào)運動控制、高速數(shù)據(jù)采集、以及需要和機器視覺聯(lián)動的硬實時環(huán)節(jié)。這些場景的共同特點是遲到一個周期就出事而不是平均速度夠快就行。不適合的場景也明確純算力密集任務、圖形界面為主的產(chǎn)品、或者團隊里沒人愿意維護一個打了私有補丁的內(nèi)核。Xenomai 這套東西一旦跑起來你就得一直維護這個打了 ipipe 補丁的內(nèi)核升級內(nèi)核等于重新做一遍全套測試這個維護成本在立項前就必須想清楚。2. 版本矩陣3.0.5/3.0.9 與 4.9.38/4.9.90 怎么對上號2.1 版本搭配總表先說這次涉及的組合實際用過兩套配置一套是量產(chǎn)設備的基礎版本一套是后來升級修訂的版本。Xenomai 版本Linux 內(nèi)核ipipe 補丁示例說明3.0.54.9.38ipipe-core-4.9.38-x86-3.patch早期量產(chǎn)組合穩(wěn)定但有一些 SMP 下的延遲抖動3.0.94.9.90ipipe-core-4.9.90-x86-2.patch升級后的組合高負載下調(diào)度更穩(wěn)3.0.94.9.90ipipe-core-4.9.90-arm-*.patchARM 平臺對應版本注意架構(gòu)不能混用上面補丁文件名末尾的修訂號以官網(wǎng)實際發(fā)布為準不同時期會有 -2、-3、-4 這類后綴差別但命名規(guī)則就是這樣ipipe-core-內(nèi)核版本-架構(gòu)-修訂號。這里最要命的一條規(guī)則是ipipe 補丁必須精確匹配內(nèi)核的小版本號。4.9.38 的補丁打不到 4.9.90 上4.9.90 的補丁也打不到 4.9.38 上一個點版本不對就是一片 hunk failed。因為 ipipe 補丁會改動內(nèi)核的kernel/irq/、arch/x86/kernel/這類核心目錄上下文對不上直接拒絕。2.2 3.0.5 到 3.0.9升級到底賺了什么兩個 Xenomai 版本的用戶態(tài) API 基本兼容所以從 3.0.5 升到 3.0.9 的成本不高但收益是實打?qū)嵉摹?.0.9 修了一批 Cobalt 核在 SMP 環(huán)境下的中斷親和性問題在我這邊的現(xiàn)象是原來 3.0.5 在高負載下偶爾冒出來的 50us 左右的抖動升級之后基本消失。另外 3.0.9 對 4.9 系列較新的點版本支持更完整所以如果內(nèi)核要升到 4.9.90Xenomai 這邊也建議同步升到 3.0.9。內(nèi)核為什么選 4.9 LTS 系列也很簡單4.9 是長期維護版本ipipe 補丁覆蓋全工控行業(yè)當時的主流物料就是它在支撐。4.9.38 和 4.9.90 都屬于這個分支的不同維護點后者修掉了很多前者時期的驅(qū)動和調(diào)度問題。如果項目從零開始直接上 3.0.9 4.9.90 就好別再用 3.0.5 折騰自己。3. 關鍵配置實錄打上 ipipe 補丁本身就有一堆坑3.1 補丁獲取與打補丁ipipe 補丁可以從 Xenomai 官網(wǎng)的 ipipe 下載目錄拿也可以用git拉取對應倉庫。下載時先確認兩件事架構(gòu)對不對、內(nèi)核點版本對不對。x86_64 和 ARM 的補丁文件不是同一個在 x86 機器上下載 ARM 補丁打上去大概率直接報錯。拿到補丁后的標準操作如下# 假設已經(jīng)下載好 linux-4.9.90.tar.xz 和 ipipe-core-4.9.90-x86-2.patch tar xf linux-4.9.90.tar.xz cd linux-4.9.90 patch -p1 ../ipipe-core-4.9.90-x86-2.patch打完補丁馬上檢查有沒有.rej文件殘留有就說明部分補丁沒打進去find . -name *.rej -o -name *.orig如果這條命令有輸出千萬不要繼續(xù)往下編譯先把補丁問題解決再說。常見情況是下載到了網(wǎng)頁 HTML 而不是原始文件file命令看一眼就能識別。3.2 內(nèi)核配置的關鍵項打完補丁之后內(nèi)核配置菜單里會多出一個 IRQ pipeline 相關菜單。用make menuconfig打開搜索IPIPE能看到對應選項。我這邊實際用下來的配置項推薦如下配置項推薦值原因CONFIG_IPIPEy打開中斷流水線這是 Xenomai 運行的前提CONFIG_HZ_1000y時鐘頻率提到 1000HzLinux 域響應更及時CONFIG_PREEMPTyLinux 域任務搶占粒度更細減少對實時的干擾CONFIG_CPU_FREQn關閉 CPU 調(diào)頻避免頻率切換造成延遲抖動CONFIG_CPU_IDLEn關閉 CPU 深度睡眠省得喚醒時卡頓CONFIG_SUSPEND / HIBERNATIONn關閉睡眠喚醒路徑CONFIG_IPIPE_DEBUGn調(diào)試版會拖慢實時路徑量產(chǎn)必須關CONFIG_LOCKDEPn鎖檢測開銷極大會讓延遲數(shù)據(jù)完全失真配置的時候建議從發(fā)行版自帶的 config 出發(fā)而不是make defconfig從頭開始cp /boot/config-$(uname -r) .config make olddefconfig make menuconfig這樣能少踩很多驅(qū)動缺失的坑。改完配置后編譯make -j$(nproc) bzImage modules sudo make modules_install sudo cp arch/x86/boot/bzImage /boot/vmlinuz-4.9.90-xenomai裝完內(nèi)核記得更新引導配置然后重啟確認內(nèi)核版本里帶-ipipe后綴。如果重啟后uname -r還是普通版本號先查引導配置有沒有生效。3.3 工具鏈提醒4.9 內(nèi)核配太新的 GCC 容易編譯報錯比如 GCC 11 之后常見各種隱含聲明、頭文件不兼容問題。這不是 Xenomai 的問題是老內(nèi)核和新編譯器的兼容性問題。我這邊用的是 GCC 7/8 附近的工具鏈一次通過。如果你只有新 GCC可以先試試在舊內(nèi)核源碼里補一些兼容性頭文件但最省事的方案還是換工具鏈。4. 從編譯到跑通 latency用戶態(tài)安裝實操4.1 Xenomai 用戶態(tài)編譯內(nèi)核搞定后接著編譯 Xenomai 用戶態(tài)。下載 xenomai-3.0.9 源碼包后tar xf xenomai-3.0.9.tar.bz2 cd xenomai-3.0.9 ./scripts/bootstrap ./configure --with-corecobalt --enable-smp --prefix/usr/xenomai make -j$(nproc) sudo make installbootstrap這步很多人會漏掉。直接從官網(wǎng)下載的 release 包一般帶 configure 腳本但如果是從 git 倉庫拉的最新代碼必須先跑 bootstrap 生成 configure。--with-corecobalt是選擇雙內(nèi)核模式這也是 Xenomai 實時性最強的工作方式。編譯期間一個常見問題是缺 autoconf、automake、libtool 這類工具發(fā)行版里裝一下即可。別小看這一步bootstrap 階段報錯基本都是這些基礎工具版本太老或者沒裝全。4.2 環(huán)境變量與模塊加載安裝完成后把環(huán)境變量寫進當前會話或用戶的腳本里export XENO_BASE/usr/xenomai export PATH$XENO_BASE/bin:$PATH export XENO_CONFIG$XENO_BASE/bin/xeno-config加載內(nèi)核模塊和啟動 Xenomai 服務sudo $XENO_BASE/sbin/start.sh dmesg | grep -i xenomai正常情況下 dmesg 里能看到 Xenomai/Cobalt 相關初始化信息。如果 start.sh 報模塊加載失敗直接看下一章的 BUG 記錄這恰恰是最容易卡住的環(huán)節(jié)。4.3 第一次跑 latency用戶態(tài)跑通后第一個要測的就是自帶 latency 示例這是判斷環(huán)境是否干凈的黃金標準sudo /usr/xenomai/demos/latency -T 60 -p 1000 -s -i-p 1000表示測試周期 1000us-T 60表示跑 60 秒-s是同步模式更接近真實控制循環(huán)的觸發(fā)方式-i會在測試結(jié)束時輸出更多統(tǒng)計信息。運行后屏幕會滾動類似這樣的數(shù)據(jù)RTT| 00:00:10 (periodic, 1000 us, 10000 samples, avg 2 us, max 9 us)判斷標準很簡單x86_64 平臺上 max 值應該在 10us 以內(nèi)大部分時候是個位數(shù)ARM 平臺看主頻和中斷控制器質(zhì)量但 max 原則上也不該超過 50us。如果你的 max 數(shù)值在這個量級以上直接跳到后面的排查章節(jié)你的環(huán)境肯定有地方?jīng)]配置干凈。5. BUG 記實上補丁打不上、模塊加載失敗、啟動即卡死5.1 現(xiàn)象patch 報 hunk FAILED這是新手必踩的第一個坑我也沒躲過去?,F(xiàn)象是執(zhí)行patch -p1后屏幕一堆Hunk #1 FAILED at 1042這類輸出或者干脆一路 reject。排查鏈如下第一步file查看補丁文件類型如果是 HTML document 說明下載到了網(wǎng)頁而不是原始文件重新下載 raw 版本。第二步核對架構(gòu)。x86 的補丁里會看到 arch/x86 的改動ARM 補丁則涉及 arch/arm看 20 行就能區(qū)分。第三步核對內(nèi)核點版本。tar xf出來的目錄名必須和補丁文件名里的版本完全一致比如 linux-4.9.90 的源碼樹就只能打 ipipe-core-4.9.90 的補丁。這個問題的根因就一個版本或來源不對。補丁不是通用的它是針對特定內(nèi)核版本生成的上下文差異。所以別去第三方博客下載補丁一定要從官方渠道拿并且和內(nèi)核版本逐字符核對。5.2 現(xiàn)象模塊 version magic 對不上內(nèi)核和用戶態(tài)都編譯完了跑 start.sh 加載模塊時報錯大意是內(nèi)核模塊的 version magic 不匹配說當前內(nèi)核是4.9.90-ipipe但模塊期望的是4.9.90或者反過來。根因在 ipipe 補丁會給內(nèi)核版本自動追加-ipipe后綴。外部模塊必須用打過補丁的內(nèi)核源碼樹編譯它的 version magic 才會帶上-ipipe。最常見的錯誤是/lib/modules/$(uname -r)/build這個軟鏈接指向了沒打補丁的干凈內(nèi)核源碼導致外部模塊編譯時用了錯誤的頭文件。處理辦法是重新確認軟鏈接指向打過補丁的那棵源碼樹然后重新make modules_install再重新編譯安裝 Xenomai 用戶態(tài)。這一步如果不重做模塊加載的報錯會反復出現(xiàn)。5.3 現(xiàn)象啟動到 I-pipe 附近卡死另一個讓人頭皮發(fā)麻的問題是內(nèi)核引導到剛初始化中斷流水線的地方就沒了輸出反復重啟都一樣。如果手頭是 x86_64 工控機常見原因是串口 console 驅(qū)動與其他驅(qū)動在中斷接管上沖突或者配置里開了某些調(diào)試選項導致早期初始化路徑變得極慢。我當時的排查過程是先在引導參數(shù)里加earlyprintk把啟動輸出提前打出來確認卡死的位置再把可能搶占早期中斷的設備驅(qū)動從內(nèi)核里摘掉比如串口 DMA、某些 GPIO 控制器最終定位到是設備樹里一個中斷屬性與 ipipe 補丁的預期不一致。這個問題的通用排查路徑是先確認卡死函數(shù)再逐個禁用可疑驅(qū)動最后對比設備樹中斷配置。ARM 平臺尤其常見x86 平臺相對少但排查思路一樣。6. BUG 記實下延遲毛刺、外設中斷和用戶態(tài)段錯誤6.1 固定周期毛刺是內(nèi)核在做家務latency 測試最大的敵人之一是周期性出現(xiàn)的尖峰?,F(xiàn)象非常典型平時 max 只有 5us但每隔固定時間就冒出來一個 100us 左右的尖峰像時鐘一樣準。這種固定周期的毛刺優(yōu)先懷疑三個東西NMI watchdog、RCU 回調(diào)、tick 處理。NMI watchdog 會定期在每個 CPU 上觸發(fā)不可屏蔽中斷這個中斷在實時域看來就是天外來客RCU 的回調(diào)如果落在實時任務所在的核心上也會周期性制造延遲。處理手段如下# 關閉 NMI watchdog sysctl -w kernel.nmi_watchdog0 # 引導參數(shù)里把 CPU1 從調(diào)度和 RCU 家務里摘出來 # isolcpus1 rcu_nocbs1 # 然后實時任務固定綁在 CPU1 上 taskset -c 1 /usr/xenomai/demos/latency -T 60 -p 1000 -s -i當時我加了這幾個參數(shù)之后那個如鐘表般準時的尖峰就消失了。這個問題的本質(zhì)是Linux 域的周期性家務打擾了 Cobalt 域方向比具體參數(shù)更重要。6.2 外設一跑實時就崩中斷親和性另一種高頻場景安靜環(huán)境下 latency 一切正常一開網(wǎng)卡流量max 值從個位數(shù)直接跳到幾百微秒甚至毫秒級。我遇到過一跑 iperf 實時就報警的項目排查到后面發(fā)現(xiàn)是網(wǎng)卡中斷和實時任務擠在了同一個 CPU 核心上。x86 平臺的irqbalance會動態(tài)輪轉(zhuǎn)中斷到各核心但它的調(diào)度邏輯不會考慮你的實時任務綁在哪。解決辦法是把實時任務和中斷分別固定在兩個核上# 實時任務綁 CPU1 taskset -c 1 /usr/xenomai/demos/latency -T 60 -p 1000 -s -i # 把網(wǎng)卡中斷固定到 CPU0 # 先 /proc/interrupts 找到網(wǎng)卡的 IRQ 號假設是 16 echo 1 /proc/irq/16/smp_affinityUSB 控制器也有同樣的問題插拔 U 盤、移動鼠標時如果延遲出現(xiàn)毛刺把對應 IRQ 挪走即可。這個排查方向?qū)θ魏瓮庠O一動作實時就崩的問題都適用先看/proc/interrupts確認中斷落在哪個核上再調(diào)整親和性。6.3 十幾毫秒巨坑查電源管理最陰間的 BUG 往往在電源管理這里?,F(xiàn)象是測 30 秒沒事跑 10 分鐘突然冒出一個 10ms 級別的巨坑而且間隔非常不規(guī)則。如果你先入為主地去查中斷大概率一無所獲因為問題根本不在中斷而在CPU 頻率和 C-state 深度睡眠。x86 平臺默認的 CPU 調(diào)頻策略是 powersave負載降下來就會自動降頻實時任務被喚醒時 CPU 正處在低頻或者深睡眠狀態(tài)喚醒延遲直接拉滿。處理方式分三層第一層BIOS 里關閉 C3/C6 等深度休眠狀態(tài)。第二層內(nèi)核配置里關掉 CONFIG_CPU_FREQ 和 CONFIG_CPU_IDLE。第三層運行時強制 performance governorcpupower frequency-set -g performance我在這上面耗時最久一度以為是網(wǎng)卡驅(qū)動問題。后來把電源管理全部關掉所有不規(guī)則大坑全部消失。x86 平臺做實時先關電源管理再談別的這句話我現(xiàn)在逢人就講。6.4 用戶態(tài)一碰實時 API 就段錯誤還有一個用戶態(tài)的高頻問題用 xeno-config 編譯出來的程序一調(diào)用rt_task_create就段錯誤普通 Linux 程序完全正常。這個現(xiàn)象的根因幾乎都在編譯環(huán)境或權限。編譯環(huán)境問題xeno-config --cflags給出的編譯參數(shù)必須完整帶進你的構(gòu)建過程。很多人只抄了-I/usr/xenomai/include漏了-D_GNU_SOURCE這類宏定義結(jié)果頭文件里的聲明和庫實現(xiàn)對不上運行直接崩。正確的做法是用命令替換export XENO_USER_CFLAGS$(/usr/xenomai/bin/xeno-config --cflags) export XENO_USER_LDFLAGS$(/usr/xenomai/bin/xeno-config --ldflags) # Makefile 里通過 $(shell ...) 引入不要手抄權限問題非 root 用戶跑實時程序需要把用戶加入 Xenomai 的允許組否則內(nèi)核會拒絕你創(chuàng)建實時任務。方法是在引導參數(shù)里加xenomai.allowed_group1000或者運行時把組 ID 寫進/proc/xenomai/group。這個問題別看簡單現(xiàn)場項目里因為漏了這一步導致程序段錯誤、然后懷疑整了半天內(nèi)核配置的案例非常普遍。7. 延遲亂飛的系統(tǒng)化排查思路7.1 先分域Linux 域問題 vs Cobalt 域問題踩過一圈 BUG 之后我最大的體會是延遲問題排查前先分清是 Linux 域的問題還是 Cobalt 域的問題。實時任務里如果調(diào)用了printf、open、malloc這類 Linux 系統(tǒng)服務就會發(fā)生域切換切換本身就有開銷次數(shù)多了延遲自然難看。這不是內(nèi)核的錯是代碼寫得不對。Cobalt 域里做日志應該用rt_printk這類實時安全接口數(shù)據(jù)交換用預分配的內(nèi)存池文件系統(tǒng)操作全部放到普通 Linux 線程里做。很多延遲莫名其妙變高的案例最后定位到就是實時任務里多了一行printf。7.2 癥狀-懷疑對象對照表現(xiàn)場排查時我習慣拿一張表對著看效率比瞎猜高得多癥狀優(yōu)先懷疑對象驗證手段固定周期尖峰NMI watchdog、RCU、tick關閉 watchdogisolcpus/rcu_nocbs 隔離外設一動作就崩中斷親和性/proc/interrupts 查核調(diào) smp_affinity不規(guī)則十幾 ms 大坑電源管理、C-state關 CPUFreq、關 C-stateperformance governor用戶態(tài)段錯誤編譯參數(shù)、運行權限檢查 xeno-config cflags/ldflags、allowed_group一跑負載就抖SMP 調(diào)度、高優(yōu)先級任務干擾綁核、調(diào)整任務優(yōu)先級、隔離核這張表就是前面所有 BUG 的濃縮版。遇到新問題先對表對不上再往下挖。7.3 壓測環(huán)境干凈化清單做延遲測試前先把環(huán)境搞干凈否則你測出來的數(shù)據(jù)全是干擾沒法定位。我自己的固定流程如下SSH 登錄而不是本地圖形界面把桌面、合成器全部停掉。關閉 NMI watchdogsysctl -w kernel.nmi_watchdog0。關閉電源管理BIOS 層面和內(nèi)核層面同時處理。臨時斷開網(wǎng)卡或把網(wǎng)卡中斷固定到非實時核。實時任務用taskset綁核并確認沒有其他高優(yōu)先級進程落在同一個核上。每次測試記錄dmesg、/proc/interrupts和latency -h直方圖存成帶日期的文件備查。這個流程跑下來如果還有異常那基本可以確定是硬件或者驅(qū)動層面的問題而不是配置層面的問題。8. 一點經(jīng)驗小結(jié)后來我把新項目切到了更新的內(nèi)核與 Xenomai 組合但每次新設備出現(xiàn)莫名的延遲上漲還是會翻出當年這套 3.0.5/3.0.9 4.9.38/4.9.90 的排查筆記思路永遠是同一個順序先看電源管理、再看中斷親和、再看 RCU。最后分享一個讓我少走很多彎路的小習慣——每套內(nèi)核驗證穩(wěn)定后把dmesg、/proc/interrupts、latency直方圖連同內(nèi)核的.config一起打包備份命名里帶上日期和內(nèi)核版本。這套證據(jù)鏈在你三個月后回來處理現(xiàn)場問題時比任何排查工具都管用。