俞冠中,韋雄,田青旺,史旭明
(國核自儀系統(tǒng)工程有限公司,上海 200241)
龍芯2K1000 處理器[1-2]是一款面向工業(yè)自動化與工業(yè)控制應(yīng)用場景的高性能低功耗通用處理器,基于MIPS64架構(gòu),采用40 nm 制造工藝[3],主頻最高1 GHz,功耗小于5 W,支持64 位DDR2/3-1066 內(nèi)存,提供SPI、UART、I2S、I2C、USB2.0 等通用外設(shè)接口。
目前市場上,龍芯2K1000 板卡一般預(yù)裝Loongnix 操作系統(tǒng)。Loongnix 操作系統(tǒng)是一種基于Linux 內(nèi)核的圖形化界面操作系統(tǒng)。和Linux 系統(tǒng)一樣,Loongnix 系統(tǒng)也是分時系統(tǒng)[4],不能滿足對實時性要求較高的工業(yè)自動化場景(如電站控制[5-6])的要求。因此,需要針對Linux內(nèi)核影響實時性能的因素進(jìn)行實時性改造和優(yōu)化。
目前,Linux 內(nèi)核實時化的有效方法是在Linux 內(nèi)核源文件中加入實時補丁,再編譯內(nèi)核,生成Linux 實時內(nèi)核。Linux 內(nèi)核實時補丁主要有三種:RED-Linux 補丁,Kurt-Linux 補丁以及實時搶占(RT-preempt)補丁[7-8]。REDLinux 補丁是美國加州大學(xué)歐文分校(University of California Irvine,UCI)開發(fā)的一種軟實時補丁[8]。Kurt-Linux是美國堪薩斯大學(xué)(University of Kansas,KU)開發(fā)的一種Linux 實時補丁,其內(nèi)核同時運行實時任務(wù)和非實時任務(wù)時,內(nèi)核不能被搶占[8-9]。RT-Preempt 補丁是由Ingo Molnar和Thomas Gleixner 開發(fā)和維護(hù)的一種完全可搶占式內(nèi)核的硬實時補丁[10],其實時性要明顯優(yōu)于前兩種Linux實時補丁,且RT-Preempt 是開源補丁,擁有強大社區(qū)支持[11],支持Linux 內(nèi)核版本也比前兩者豐富。綜上所述,本文提出一種基于RT-Preempt-Linux 實時內(nèi)核替換Loongnix 系統(tǒng)原生Linux 內(nèi)核的方法,實現(xiàn)Loongnix 實時性優(yōu)化和實時化改造。
Linux 進(jìn)程切換機制依賴進(jìn)程用戶態(tài)和進(jìn)程內(nèi)核之間互相切換實現(xiàn)的[12]。進(jìn)程需通過系統(tǒng)調(diào)用或中斷觸發(fā)來完成進(jìn)程用戶態(tài)到進(jìn)程內(nèi)核態(tài)的切換。進(jìn)程切換時,內(nèi)核使用自旋鎖來確保數(shù)據(jù)的不沖突。進(jìn)程進(jìn)入臨界區(qū)操作數(shù)據(jù)時,其他進(jìn)程只能阻塞,那么任務(wù)的時間確定性就無法保證[13]。所以,標(biāo)準(zhǔn)Linux 內(nèi)核的臨界區(qū)是不可搶占的。
Linux 中斷響應(yīng)處理分為頂半(top-halves)部和底半(buttom-halves)部[11],也稱為上半部和下半部。上半部屬于硬中斷,會關(guān)閉中斷,屏蔽其他任何中斷請求。在關(guān)閉中斷時,系統(tǒng)外部事件無法得到響應(yīng),導(dǎo)致任務(wù)響應(yīng)延遲。若標(biāo)準(zhǔn)Linux 出現(xiàn)大量外部IO 事件(如磁盤操作[11]),其他任務(wù)的延時時間會大大增加。
優(yōu)先級反轉(zhuǎn)(Priority Inversion)[13-14]是指高優(yōu)先級的任務(wù)被低優(yōu)先級的任務(wù)阻塞,反而中等優(yōu)先級的任務(wù)先于高優(yōu)先級的任務(wù)執(zhí)行的現(xiàn)象。低優(yōu)先級的進(jìn)程PL 首先執(zhí)行,并占用了共用資源Rsrc,此時高優(yōu)先級進(jìn)程PH開始執(zhí)行,進(jìn)程PL 掛起。當(dāng)進(jìn)程PH 嘗試獲取Rsrc時,因Rsrc 被進(jìn)程PL 占據(jù),進(jìn)程PH 也掛起,進(jìn)程PL 恢復(fù)運行。此時,不需要Rsrc 的中等優(yōu)先級進(jìn)程PM 就緒運行,進(jìn)程PL 掛起。進(jìn)程PM 執(zhí)行結(jié)束后,進(jìn)程L 恢復(fù)運行,直至放棄Rsrc,此時,高優(yōu)先級進(jìn)程PH 才獲得CPU使用權(quán)。中優(yōu)先級進(jìn)程PM 先于高優(yōu)先級進(jìn)程PH 獲取CPU 使用權(quán),優(yōu)先級發(fā)生反轉(zhuǎn)。優(yōu)先級反轉(zhuǎn)對操作系統(tǒng)實時性危害很大,增加了任務(wù)調(diào)度時間的不確定性,嚴(yán)重時引起系統(tǒng)崩潰[8]。目前,標(biāo)準(zhǔn)Linux 內(nèi)核并無應(yīng)對優(yōu)先級反轉(zhuǎn)的機制。
RT-Preempt 補丁使用優(yōu)先級可繼承的互斥鎖(rt_mutex)重新實現(xiàn)自旋鎖來實現(xiàn)內(nèi)核鎖的可搶占性[13,15]。自旋鎖spin_lock()宏函數(shù)內(nèi)用禁止遷移migrate_disable()替代禁止搶占preempt_disable(),使自旋鎖可搶占。實時自旋鎖rt_spin_lock()替代原自旋鎖_raw_spin_lock()。rt_spin_lock()函數(shù)的實現(xiàn)中調(diào)用了rt_spin_lock_fastlock()函數(shù)。rt_spin_lock_fastlock()函數(shù)中調(diào)用了might_sleep()函數(shù)。might_sleep()函數(shù)的作用是允許當(dāng)前進(jìn)程進(jìn)入睡眠狀態(tài),進(jìn)程進(jìn)入睡眠狀態(tài)則該進(jìn)程交出了CPU 的使用權(quán)。那么,進(jìn)程可以被搶占。需要指出的是內(nèi)核中非線程化的中斷不能被搶占,不能使用rt_mutex[10]。
RT-Preempt 補丁的中斷線程化處理是將中斷服務(wù)程序轉(zhuǎn)變?yōu)榭杀徊僮飨到y(tǒng)調(diào)度的線程斷線程的優(yōu)先級并不固定,用戶按需設(shè)置其優(yōu)先級,默認(rèn)優(yōu)先級為50。
中斷線程化處理包含硬中斷的線程化處理和軟中斷的線程化處理兩部分[15]。硬中斷線程化在__setup _irq()函數(shù)中實現(xiàn),__setup_irq()函數(shù)調(diào)用kthread_create()函數(shù)創(chuàng)建線程,采用先進(jìn)先出調(diào)度策略(SCHED_FIFO)。軟中斷線程化在spawn_ksoftirqd()中實現(xiàn),其線程建立過程與硬中斷線程化相同。需要指出的是:不是所有的中斷都需要中斷線程化處理,例如時鐘中斷應(yīng)為最高優(yōu)先級,不能中斷線程化處理。struct irqaction 結(jié)構(gòu)體中的flag 成員變量用來設(shè)置是否需要中斷線程化處理。
在高優(yōu)先級任務(wù)TH 等待低優(yōu)先級任務(wù)TL 占據(jù)共用資源Rsrc時,為了使低優(yōu)先級任務(wù)盡快運行并釋放Rsrc,操作系統(tǒng)會將TL 的優(yōu)先級提高到和TH 的優(yōu)先級一樣,直到TL 釋放Rsrc。當(dāng)TL 的優(yōu)先級繼承了TH 的優(yōu)先級,中優(yōu)先級任務(wù)TM 就無法搶先TH 獲得CPU 的使用權(quán)。因此,優(yōu)先級反轉(zhuǎn)就不會產(chǎn)生。RT-Preempt 補丁通過優(yōu)先級可繼承rt_mutex 實現(xiàn)優(yōu)先級繼承策略。加入RT-Preempt 補丁編譯Linux后,rt_mutex 成為Linux 核心(kernel)的組成部分。
RT-Preempt 補丁的時鐘系統(tǒng)不像依賴標(biāo)準(zhǔn)Linux 一樣依賴系統(tǒng)滴答中斷計時,而是提供了一套新的時鐘架構(gòu),可以提供納秒級的精度。標(biāo)準(zhǔn)Linux 系統(tǒng)為了提高時鐘分辨率而升高系統(tǒng)滴答中斷的頻率情況不會在RT-Preempt-Linux 出現(xiàn),從而避免了系統(tǒng)符合變重性能降低的發(fā)生。
在一個時刻里,一個CPU 只能執(zhí)行一個任務(wù)。多個任務(wù)共享一個 CPU 需要依賴上下文切換(Context Switch)。上下文切換時間是指CPU 從一個任務(wù)切換到另一個任務(wù)所需的時間開銷。上下文切換時間決定了任務(wù)調(diào)度速度。因此,上下文切換時間是衡量操作系統(tǒng)實時性的關(guān)鍵指標(biāo)[16]。Linux 支持多進(jìn)程運行,所以,Linux 的上下文切換時間就是進(jìn)程切換時間。
進(jìn)程切換時間統(tǒng)計軟件的設(shè)計思路是通過讀管道(pipe)和寫管道來實現(xiàn)父子進(jìn)程之間的同步,其程序流程如圖1 所示。父進(jìn)程獲取當(dāng)前時間,把當(dāng)前時間寫入管道,后讀管道阻塞。子進(jìn)程讀管道獲取父進(jìn)程切換前的時間,再獲取當(dāng)前時間,計算進(jìn)程切換時間。進(jìn)程調(diào)度策略采用時間片輪轉(zhuǎn)(SCHED_RR),進(jìn)程優(yōu)先級設(shè)置為99,切換統(tǒng)計次數(shù)設(shè)定為1 000 次。每運行滿1 000次,打印切換平均用時,打印切換最大用時和切換最小用時。
圖1 進(jìn)程切換時間統(tǒng)計軟件流程圖
線程是進(jìn)程內(nèi)共享進(jìn)程資源的一個最小執(zhí)行單元。線程切換時間大小體現(xiàn)了一個進(jìn)程內(nèi)的任務(wù)調(diào)度速度。因此,線程切換時間也是評判操作系統(tǒng)實時性能的重要標(biāo)志。
線程切換時間統(tǒng)計軟件由三個模塊組成:主線程,線程1 和線程2。主線程內(nèi)初始化功能所需的全局變量,如timespec 結(jié)構(gòu)體對象等,初始化信號量,創(chuàng)建線程1和線程2,計算線程切換時間平均值,統(tǒng)計線程切換時間的最大值和最小值,并打印線程切換的平均用時、線程切換的最大用時和線程切換時間的最小用時。
線程1 的邏輯設(shè)計圖如圖2 所示,線程1 先等待信號量1,收到信號量1 后獲取當(dāng)前時間,計算線程切換時間,再獲取當(dāng)前時間,最后發(fā)送信號量2。信號量2 發(fā)送后,線程2 就被喚醒執(zhí)行。在主線程中初始化信號量1時,其參數(shù)Value 設(shè)置為1,首先運行線程1。
圖2 線程切換時間統(tǒng)計軟件流程圖
線程2 的設(shè)計邏輯與線程1 相同。線程切換時間統(tǒng)計軟件通過兩個信號量實現(xiàn)線程1 和線程2 的同步,其設(shè)計思想和進(jìn)程切換時間統(tǒng)計軟件相似。
實時Loongnix 系統(tǒng)是用RT-Preempt-Linux 內(nèi)核替代Loongnix 系統(tǒng)原生標(biāo)準(zhǔn)Linux 內(nèi)核后的Loongnix 系統(tǒng)。RT-Preempt-Linux 內(nèi)核是在標(biāo)準(zhǔn)Linux 內(nèi)核源碼上加入RT-Preempt 補丁后編譯生成的。對安裝實時Loongnix 系統(tǒng)的龍芯2K1000 平臺進(jìn)行性能測試。測試分為用自設(shè)計軟件測試進(jìn)程切換時間和線程切換時間,以及用專用實時性能測試工具Cyclictest 測試任務(wù)響應(yīng)延時時間。對未替換實時內(nèi)核的Loongnix 系統(tǒng)進(jìn)行相同的性能測試并進(jìn)行比較研究。
實時Loongnix 系統(tǒng)和原生Loongnix 系統(tǒng)性能測試的軟硬件環(huán)境如表1 所示。龍芯2K1000 處理器的工作主頻設(shè)置為800 MHz。由于電站控制項目要求的應(yīng)用軟件需要在Linux 4.0 版本以上才能運行,因此,實時Loongnix系統(tǒng)選用的Linux 4.19 內(nèi)核,并在其基礎(chǔ)上加入對應(yīng)版本的RT-Preempt 補丁。
表1 測試軟硬件環(huán)境
進(jìn)程切換時間統(tǒng)計軟件的進(jìn)程優(yōu)先級設(shè)置為99,統(tǒng)計次數(shù)設(shè)定為1 000 次。在龍芯2K1000 平臺上的實時Loongnix 系統(tǒng)運行20 次獲取總共2 萬次進(jìn)程切換時間的統(tǒng)計數(shù)據(jù)。在龍芯2K1000 平臺上的原生Loongnix 系統(tǒng)進(jìn)行相同的測試。測試結(jié)果見表2。
表2 實時Loongnix 與原生Loongnix 進(jìn)程切換時間對比
實時Loongnix 進(jìn)程切換時間為微秒級,而未使用RT-Preempt-Linux 內(nèi)核的原生Loongnix 進(jìn)程切換時間達(dá)到2.51 ms。實時Loongnix 系統(tǒng)可以滿足電站控制應(yīng)用的系統(tǒng)任務(wù)切換時間不大于1 ms 的性能需求。
對實時Loongnix(實線)與原生Loongnix(虛線)進(jìn)程最大切換時間每千次切換統(tǒng)計一次的對比如圖3 所示。實時Loongnix 系統(tǒng)每千次切換的最大切換時間連線比較平滑,而原生Loongnix 系統(tǒng)每千次切換的最大切換時間連線抖動幅度比較大。因此,RT-Preempt-Linux 內(nèi)核對Loongnix 系統(tǒng)的上下文切換時間確定性提高明顯。
圖3 實時Loongnix 與原生Loongnix 最大進(jìn)程切換時間對比
線程切換時間統(tǒng)計軟件的統(tǒng)計次數(shù)設(shè)定為1 000次。在龍芯2K1000 平臺上的實時Loongnix 系統(tǒng)運行20次獲取總共2 萬次線程切換時間的統(tǒng)計數(shù)據(jù)。在龍芯2K1000 平臺上的原生Loongnix 系統(tǒng)進(jìn)行相同的測試。測試結(jié)果見表3。
表3 實時Loongnix 與原生Loongnix 線切換時間對比
實時Loongnix 線程平均切換時間和未使用RT-Preempt-Linux 內(nèi)核的原生Loongnix 線程切平均切換時間接近,但其線程切換最大用時也是微秒級的。原生loongnix的最大線程切換用時要超過4 ms。
對實時Loongnix(實線)與原生Loongnix(虛線)線程最大切換時間每千次切換統(tǒng)計一次的對比如圖4 所示。實時Loongnix 系統(tǒng)每千次切換的最大切換時間連線比較平滑且都在100 μs 左右,而原生Loongnix 系統(tǒng)每千次切換的最大切換時間連線抖動幅度大。因此,RT-Preempt-Linux 內(nèi)核對Loongnix 系統(tǒng)的線程切換時間確定性提高明顯。
圖4 實時Loongnix 與原生Loongnix 最大線程切換時間對比
Cyclictest 是一種開源的專業(yè)Linux 實時性能測試工具軟件,可以精確地測量任務(wù)喚醒延時。Cyclictest 測量線程線程時間喚醒的時間間隔,這個實際的時間間隔與線程睡眠設(shè)定時間的差就是任務(wù)喚醒時間的延時。這個延時由定時器中斷延時和線程調(diào)度延時組成。中斷響應(yīng)時間和保存上下文的時間決定了中斷延時的大小。本次實驗使用Cyclictest 1.0。
4.4.1 單線程測試
Cyclictest 測試指令為:sudo ./cyclictest -l100000 -m-t1 -n -p90 -i200 -h2000 -q,其中,-l(loops)為循環(huán)個數(shù),本次測試設(shè)定為100 000,缺省為0;-m(mlockall)為鎖定當(dāng)前和未來的內(nèi)存分配;-t[NUM](threads=NUM)為啟動線程個數(shù),本次測試為單線程測試,故設(shè)定為1;-n(nanosleep)使用精度為納秒的睡眠時間設(shè)置;-p(prio)為線程設(shè)置的優(yōu)先級,本次實驗優(yōu)先級設(shè)置為90;-i(interval)為線程的時間間隔,本次實驗設(shè)置為200 μs,缺省為1 000 μs;-h(histogram)為記錄延時時間,本次實驗跟蹤2 000μs以內(nèi)的延時;-q(quiet)為退出前打印結(jié)果。實時Loongnix 的測試結(jié)果如圖5 所示,原生Loongnix 的測試結(jié)果如圖6 所示。
圖5 實時Loongnix 系統(tǒng)Cyclictest 單線程測試結(jié)果
圖6 原生Loongnix 系統(tǒng)Cyclictest 單線程測試結(jié)果
RT-Preempt-Linux 內(nèi)核替換后成為實時系統(tǒng)的loongnix 系統(tǒng)單線程最大延遲時間微秒級。未實時化改造的原生Loongnix 系統(tǒng)單線程最大延時超過4 ms。
4.4.2 多線程測試
Cyclictest 測試指令為:sudo ./cyclictest -l100000 -m-t5 -n -p90 -i200 -q。其中線程數(shù)量設(shè)置為5。實時Loongnix 的測試結(jié)果如圖7 所示,原生Loongnix 的測試結(jié)果如圖8 所示。
圖7 實時Loongnix 系統(tǒng)Cyclictest 多線程測試結(jié)果
圖8 原生Loongnix 系統(tǒng)Cyclictest 多線程測試結(jié)果
RT-Preempt-Linux 內(nèi)核替換后成為實時系統(tǒng)的loongnix 系統(tǒng)5 個線程最大延遲時間均為微秒級。未實時化改造的原生Loongnix 系統(tǒng)所有5 個線程最大延時都大于2 ms。
本文首先分析了Loongnix 系統(tǒng)的標(biāo)準(zhǔn)Linux 內(nèi)核影響實時性能的3 個重要因素,探究了RT-Preempt-補丁的實時性優(yōu)化原理,提出一種基于RT-Preempt-Linux 實時內(nèi)核替換Loongnix 系統(tǒng)原生Linux 內(nèi)核的方法,實現(xiàn)Loongnix 實時性優(yōu)化和實時化改造,給出了兩種實時性能測試軟件的設(shè)計方法,并用自設(shè)計軟件和專用實時性測試工具軟件對實時化的Loongnix 系統(tǒng)和原生Loongnix系統(tǒng)進(jìn)行實時性能測試與分析。測試結(jié)果表明,改造后的Loongnix 系統(tǒng)的實時性較改造前有了大幅提升,進(jìn)程切換時間、線程切換時間以及任務(wù)延時都遠(yuǎn)小于原生Loongnix 系統(tǒng),都能達(dá)到微秒級。