背景
有一個項目對實時性要求比較高,於是在linux內核上打了RT_PREEMPT補丁。
最終碰到的一個問題是,芯片本身性能不強,CPU資源不足,急需優化。
初步分析
看了下cpu占用率,除了主應用之外,有一個名為irq/38-twi0的進程引起了我們的注意,因為它竟然占據了10%的cpu。
這個irq開頭的進程是做什么的呢?原來這是一個被線程化了的中斷服務程序,負責處理i2c中斷的。這個項目i2c總線上掛載了多個設備,壓力是比較大的。
第一個想法是能否減少設備數量或者減低采集頻率,但這會影響到應用的算法表現,暫時不考慮。
第二個想法是優化代碼,但打開中斷服務程序的源碼一看,其實現非常簡單,基本就只是從硬件寄存器中把接收到的數據取出來而已,看來從這里入手也希望不大。
再仔細想想,這個進程執行的操作這么簡單,CPU占用率卻這么高,那么主要就是因為其執行的頻率過高,每次執行其實都會伴隨着進程調度/上下文切換帶來的開銷,這部分是否可以進行優化呢。
中斷線程化回顧
讓我們來回顧下中斷線程化的知識。
在Linux上,中斷的優先級比進程高,一旦中斷過來普通進程實時進程通通都要讓路,讓CPU先運行對應的中斷處理程序,這就會對實時性造成很大的影響。
為了解決這個由中斷帶來的實時性問題,或者說由不確定運行時長的中斷服務程序帶來的實時性問題,RT_PREEMPT補丁引入了中斷線程化的機制。
中斷線程化之后,中斷來了雖然還是會打斷實時進程,但所執行的操作只是喚醒中斷線程,原本的中斷服務程序被放到了一個內核線程中,延遲執行。
這樣中斷執行的速度就很快也很有確定性,實時進程被打斷后可以迅速再次運行,至於中斷服務程序,就在優先滿足實時進程的情況下,再被調度執行。
從中斷線程化的初衷看,當前這種情況根本就不適用。
1.這個中斷服務程序非常簡單,沒必要線程化。強行線程化對實時性的改善不大,反而會帶來不必要的開銷。
2.這個中斷服務程序非常關鍵,其中采集的數據的實時性也非常重要,不應該被延遲執行。中斷切換回實時進程后,實時進程依賴這些數據,還是要等這個進程把數據取出。
解決
解決方式很簡單,對於這個具體的中斷,取消線程化,讓它變回一個朴素的中斷。中斷線程化的機制雖好,也要分情況來使用,不然反而會造成系統的巨大負擔。
代碼改動是在request_irq時,傳入IRQF_NO_THREAD標志,即可避免這個中斷被線程化。
實際做改動還要注意,RT_PREEMPT使用rt_mutex代替傳統的禁用搶占的spin_lock,因此如果需要用真正的spin_lock,需要使用raw_spin_lock_t
在這個具體的例子中,中斷大概為一萬兩千次/秒,取消線程化之后,CPU占用率下降了約10%,效果顯著。
本文地址:https://www.cnblogs.com/zqb-all/p/12229990.html
歡迎點擊掃碼關注公眾號: QB雜貨鋪