雷金奎 ,邵元元 ,田 力
1.西北工業(yè)大學(xué) 第365研究所,西安 710065
2.西北工業(yè)大學(xué) 電子信息學(xué)院,西安 710072
3.西安愛(ài)生技術(shù)集團(tuán)公司,西安 710065
實(shí)時(shí)系統(tǒng)是能夠在指定的時(shí)間內(nèi)完成系統(tǒng)功能和外部或內(nèi)部、同步或異步事件做出響應(yīng)的系統(tǒng)[1]。實(shí)時(shí)系統(tǒng)中,計(jì)算的正確性不僅取決于程序的邏輯正確性,也取決于結(jié)果產(chǎn)生的時(shí)間。如果系統(tǒng)的時(shí)間約束條件得不到滿足,將會(huì)發(fā)生錯(cuò)誤,造成重大的生命財(cái)產(chǎn)損失和生態(tài)破壞。因此,實(shí)時(shí)系統(tǒng)應(yīng)該具備在事先定義的時(shí)間范圍內(nèi)識(shí)別和處理離散事件的能力;能夠處理和存儲(chǔ)控制系統(tǒng)所需要的大量數(shù)據(jù)。
提出了一種基于μC/OS-II實(shí)時(shí)操作系統(tǒng)的代碼執(zhí)行時(shí)間測(cè)量方法,并使用Microsemi SoftConsole IDE v3.3開發(fā)環(huán)境,在基于Cortex-M3核的SmartFusion主控芯片上實(shí)現(xiàn)了相關(guān)的代碼。本文測(cè)量了μC/OS-II在SmartFusion平臺(tái)下的一些實(shí)時(shí)性指標(biāo),為代碼的實(shí)時(shí)性提供了一個(gè)時(shí)間上的參考,為提高系統(tǒng)的實(shí)時(shí)性提供了保障[2-3]。
在微機(jī)系統(tǒng)中,往往可以處理多個(gè)中斷和多個(gè)任務(wù)。代碼在執(zhí)行過(guò)程中,隨時(shí)可能被中斷,然后CPU將控制權(quán)轉(zhuǎn)移給中斷服務(wù)例程;也可能將控制權(quán)轉(zhuǎn)移給其他任務(wù)。這些原因,導(dǎo)致在μC/OS-II多任務(wù)環(huán)境下,測(cè)到的代碼執(zhí)行時(shí)間的不確定性[4]。
測(cè)量一段代碼C的執(zhí)行時(shí)間,一種比較直觀的想法是這樣的:先讀取系統(tǒng)時(shí)鐘值TS,然后執(zhí)行待測(cè)代碼段C,最后再讀取時(shí)鐘值TE,得出待測(cè)代碼段的執(zhí)行時(shí)間TC=TE-TS。將在這個(gè)基本想法上進(jìn)行改進(jìn)和擴(kuò)充,使最終得到的時(shí)間TC中,去除時(shí)間測(cè)量函數(shù)自身、中斷和任務(wù)切換的影響。由于執(zhí)行代碼前后各有一次讀取當(dāng)前時(shí)刻值的操作,因此可以把前后兩次時(shí)刻讀取過(guò)程及相關(guān)的操作封裝為一對(duì)時(shí)間測(cè)量函數(shù)對(duì)。然后,可以把函數(shù)對(duì)中的兩個(gè)函數(shù)分別放到代碼段C之前和之后,執(zhí)行完代碼段C和時(shí)間測(cè)量函數(shù)對(duì)之后就能得出代碼段C的執(zhí)行時(shí)間。
考慮到時(shí)間測(cè)量函數(shù)自身、中斷和任務(wù)切換的影響,待測(cè)代碼段C的實(shí)際執(zhí)行時(shí)間(占有CPU的時(shí)間),可以表示如下:
其中,TE為代碼C結(jié)束執(zhí)行的時(shí)刻,TS為開始執(zhí)行代碼C的時(shí)刻,Tint為代碼C被中斷服務(wù)例程中斷的總時(shí)間,Tprof為測(cè)量代碼本身引入的測(cè)量總偏差。Tenter_exit為進(jìn)入中斷和退出中斷所需的時(shí)間,Toffset_int是執(zhí)行一次中斷時(shí)間測(cè)量函數(shù)對(duì)所需要的時(shí)間,Tint_exec為中斷服務(wù)代碼的執(zhí)行時(shí)間。Toffset為執(zhí)行一次時(shí)間測(cè)量代碼對(duì)所需的時(shí)間;Tin_offset是執(zhí)行時(shí)間測(cè)量函數(shù)對(duì)的過(guò)程中,兩次時(shí)刻讀取之間的代碼的執(zhí)行時(shí)間;Tin_offset_int是執(zhí)行中斷時(shí)間測(cè)量函數(shù)對(duì)的過(guò)程中,兩次時(shí)刻讀取之間的代碼的執(zhí)行時(shí)間。因?yàn)閮蓚€(gè)函數(shù)對(duì)中代碼不完全相同,所以兩次時(shí)刻讀取之間的代碼執(zhí)行時(shí)間就不同。
為了消除中斷的影響,本文對(duì)所有的中斷服務(wù)歷程的執(zhí)行時(shí)間進(jìn)行了測(cè)量,以便扣除中斷服務(wù)例程的執(zhí)行時(shí)間,得到代碼段的準(zhǔn)確執(zhí)行時(shí)間。
在多任務(wù)環(huán)境下,任務(wù)都有自己的??臻g和上下文環(huán)境。當(dāng)進(jìn)行任務(wù)切換的時(shí)候,把當(dāng)前任務(wù)的上下文環(huán)境保存到自己的棧空間,然后把下一個(gè)任務(wù)的上下文環(huán)境從它的棧空間中恢復(fù)到寄存器中,CPU的控制權(quán)就從一個(gè)任務(wù)轉(zhuǎn)移到了另一個(gè)任務(wù)。
考慮到測(cè)量時(shí)間的過(guò)程中,可能會(huì)發(fā)生任務(wù)切換、中斷等,給每個(gè)任務(wù)分配一個(gè)時(shí)間測(cè)量棧。用隨著任務(wù)的切換而切換棧,來(lái)處理多任務(wù)下的時(shí)間測(cè)量問(wèn)題。如圖1所示,任務(wù)task0、task1、…、task N的時(shí)間測(cè)量棧分別是task0_stack、task1_stack、…、task N_stack,指針數(shù)組task_stk_top[]保存著未處于運(yùn)行狀態(tài)的任務(wù)的時(shí)間測(cè)量棧棧頂,指針cur_stk_top跟蹤著當(dāng)前運(yùn)行的那個(gè)任務(wù)的時(shí)間測(cè)量棧棧頂。
當(dāng)任務(wù)切換的時(shí)候,比如從task1切換到task0,把cur_stk_top保存到task_stk_top[1],然后將task_stk_top[0]賦給cur_stk_top。這樣,當(dāng)前活躍的就是task0的棧了。
任務(wù)的時(shí)間測(cè)量棧如圖2所示,它的棧元素是結(jié)構(gòu)體struct cp_time_prof_node_t,定義如下:
圖1 多任務(wù)環(huán)境下的時(shí)間測(cè)量棧
圖2 每個(gè)任務(wù)的??臻g
其中,成員變量Ts是待測(cè)代碼段的開始時(shí)刻;Terr是中斷、任務(wù)切換和時(shí)間測(cè)量代碼引入的時(shí)間測(cè)量偏差,即式(1)中的Tint+Tprof;pnode是前一個(gè)棧元素的指針。
待測(cè)量代碼執(zhí)行完畢后,讀取當(dāng)前時(shí)刻Te,并根據(jù)式(1)、式(2)和式(3)算出它的執(zhí)行時(shí)間,并把執(zhí)行時(shí)間等信息保存到數(shù)組struct cp_cp_time_prof_t cp_time_prof[],對(duì)最終結(jié)果進(jìn)行保存。它的定義如下所示,其中,成員變量nb_of_states是待測(cè)代碼段總共測(cè)量的次數(shù),total_exec_time是總共執(zhí)行時(shí)間,max_time、min_time分別是最大執(zhí)行時(shí)間和最小執(zhí)行時(shí)間。
本文定義了五個(gè)函數(shù),定義及功能:
函數(shù)time_init()用于初始化與時(shí)間測(cè)量相關(guān)的數(shù)據(jù)。
這一對(duì)函數(shù),構(gòu)成測(cè)量代碼執(zhí)行時(shí)間的函數(shù)對(duì),用于測(cè)量代碼的執(zhí)行時(shí)間。
這一對(duì)函數(shù),構(gòu)成測(cè)量中斷服務(wù)代碼執(zhí)行時(shí)間的函數(shù)對(duì),用于測(cè)量中斷服務(wù)代碼的執(zhí)行時(shí)間,并把對(duì)被中斷的代碼的執(zhí)行時(shí)間上的影響記錄下來(lái),方便被中斷的代碼計(jì)算自身的執(zhí)行時(shí)間。
μC/OS-II中,每次切換任務(wù)上下文的時(shí)候要調(diào)用這個(gè)函數(shù)。這個(gè)鉤子函數(shù)在任務(wù)切換時(shí),切換任務(wù)所使用的時(shí)間測(cè)量棧。
測(cè)量代碼執(zhí)行時(shí)間的流程:
(1)調(diào)用cp_time_init()進(jìn)行時(shí)間測(cè)量相關(guān)代碼初始化;
(2)調(diào)用cp_time_prof_start(cp_time_prof_id_t id)開始測(cè)量,參數(shù)id是以后測(cè)量結(jié)果在數(shù)組cp_time_prof[]中的存儲(chǔ)位置的索引;
(3)執(zhí)行待測(cè)代碼段;
(4)調(diào)用 cp_time_prof_end(cp_time_prof_id_t id)結(jié)束測(cè)量;
(5)轉(zhuǎn)步驟(2),開始測(cè)量下一段代碼的執(zhí)行時(shí)間。
函數(shù)void cp_time_init()對(duì)時(shí)間測(cè)量相關(guān)數(shù)據(jù)的初始化流程:
(1)初始化系統(tǒng)時(shí)鐘;
(2)屏蔽所有中斷;
(3)初始化所有任務(wù)的時(shí)間測(cè)量棧、棧頂指針task_stk_top[]和當(dāng)前活躍任務(wù)的棧頂cur_stk_top;
(4)初始化存儲(chǔ)測(cè)量結(jié)果的數(shù)組cp_time_prof[],令其所有成員存儲(chǔ)的最大執(zhí)行時(shí)間為0,最小執(zhí)行時(shí)間為(unsigned)-1,總執(zhí)行時(shí)間為0,統(tǒng)計(jì)次數(shù)為0;
(5)計(jì)算 Toffset、Tin_offset、Toffset_int和 Tin_offset_int的值;
(6)除屏蔽所有中斷。
函數(shù)void cp_time_prof_start(cp_time_prof_id_t id)的流程:
(1)屏蔽所有中斷;
(2)讀取當(dāng)前系統(tǒng)時(shí)刻;
(3)入棧,用新的棧頂元素的成員變量Ts保存當(dāng)前時(shí)刻,新棧頂元素的成員Terr賦Tin_offset,pnode賦上一個(gè)元素的地址形成鏈表,把輸入?yún)?shù)賦給id;
(4)除屏蔽所有中斷。
函數(shù) void cp_time_prof_end(cp_time_prof_id_t id)的流程:
(1)屏蔽所有中斷;
(2)讀取當(dāng)前系統(tǒng)時(shí)刻,記為Te;
(3)設(shè)待測(cè)代碼執(zhí)行時(shí)間為Tc,則Tc=Te-Ts-Terr,把結(jié)果記錄到cp_time_prof[id]中;
(4)出棧,把原棧頂?shù)奶幚磉^(guò)程對(duì)新棧頂?shù)挠绊憰r(shí)間加到新棧頂?shù)腡err上;即把原棧頂?shù)某蓡T變量Terr加到新棧頂?shù)腡err上,時(shí)間測(cè)量函數(shù)對(duì)執(zhí)行一次引入的偏差Toffset也要加到新棧頂?shù)腡err上;
(5)除屏蔽所有中斷。
void cp_time_prof_start_int(cp_time_prof_id_t id)和void cp_time_prof_start(cp_time_prof_id_t id)函數(shù)的流程很相似,僅僅是給Terr賦的初值不同:
(1)屏蔽所有中斷;
(2)讀取當(dāng)前系統(tǒng)時(shí)刻;
(3)入棧,用新的棧頂元素的成員變量Ts保存當(dāng)前時(shí)刻,新棧頂元素的成員Terr賦Tin_offset_int,pnode賦上一個(gè)元素的地址形成鏈表,把輸入?yún)?shù)賦給id;
(4)除屏蔽所有中斷。
函數(shù)void cp_time_prof_end_int(cp_time_prof_id_t id)的流程:
(1)屏蔽所有中斷;
(2)讀取當(dāng)前系統(tǒng)時(shí)刻,記為Te;
(3)設(shè)待測(cè)代碼執(zhí)行時(shí)間為Tc,則Tc=Te-Ts-Terr,把結(jié)果記錄到cp_time_prof[id]中;
(4)出棧,把中斷處理過(guò)程對(duì)新棧頂?shù)挠绊憰r(shí)間加到新棧頂?shù)腡err上;即把中斷服務(wù)例程的執(zhí)行時(shí)間Tc加到新棧頂?shù)腡err上,中斷時(shí)間測(cè)量函數(shù)對(duì)執(zhí)行一次引入的偏差Toffset_int和進(jìn)入退出中斷所需的時(shí)間Tenter_exit也要加到新棧頂?shù)腡err上;
(5)除屏蔽所有中斷。
函 數(shù) void cp_time_prof_CtxSw_hook(uint32_t cur,uint32_t next)是μC/OS-II的任務(wù)切換函數(shù)void OS_TASK_SW()調(diào)用的鉤子函數(shù),它完成的操作就是當(dāng)任務(wù)切換的時(shí)候,比如從task1切換到task0,把cur_stk_top保存到task_stk_top[1],然后將 task_stk_top[0]賦給 cur_stk_top。這樣,當(dāng)前活躍的就是task0的棧了。
另外,在系統(tǒng)時(shí)鐘中斷服務(wù)例程中,需要維護(hù)系統(tǒng)時(shí)間供時(shí)間測(cè)量函數(shù)使用。
使用SoftConsole IDE v3.3集成開發(fā)環(huán)境,使Smart-Fusion主控芯片工作在100 MHz,片內(nèi)RAM 20 kB,對(duì)代碼最大優(yōu)化條件下,測(cè)量了μC/OS-II的任務(wù)切換時(shí)間、搶占時(shí)間和信號(hào)洗牌時(shí)間[5-6]。
(1)上下文切換時(shí)間(任務(wù)切換時(shí)間)[7]:μC/OS-II會(huì)調(diào)用OS_TASK_SW()進(jìn)行實(shí)際的上下文切換。OS_TASK_SW()是宏調(diào)用,含有微處理器的軟中斷指令,利用此中斷來(lái)實(shí)現(xiàn)任務(wù)之間的上下文切換。在移植μC/OS-II的時(shí)候,使用了PendSV軟中斷來(lái)實(shí)現(xiàn)任務(wù)切換,因此任務(wù)切換時(shí)間其實(shí)就是PendSV中斷服務(wù)例程的執(zhí)行時(shí)間。在中斷服務(wù)函數(shù)代碼調(diào)用void cp_time_prof_start_int()之后,緊接著調(diào)用void cp_time_prof_start(),在調(diào)用void cp_time_prof_CtxSw_hook()之前,調(diào)用 void cp_time_prof_end(),得出PendSV中斷前半部分的執(zhí)行時(shí)間t1。在調(diào)用void cp_time_prof_CtxSw_hook()之后,緊接著調(diào)用void cp_time_prof_start(),并在中斷服務(wù)函數(shù)代碼調(diào)用void cp_time_prof_end_int()之前,調(diào)用void cp_time_prof_end(),得到PendSV中斷后半部分的執(zhí)行時(shí)間t2。最后算出任務(wù)切換時(shí)間t1加t2再加上進(jìn)入和退出中斷的時(shí)間得到總時(shí)間0.75μs。
(2)搶占時(shí)間[8]:搶占時(shí)間即系統(tǒng)將控制權(quán)從低優(yōu)先級(jí)的任務(wù)轉(zhuǎn)移到高優(yōu)先級(jí)任務(wù)所花費(fèi)的時(shí)間。在μC/OS-II中,調(diào)用函數(shù)void OS_Sched()來(lái)尋找當(dāng)前就緒的最高優(yōu)先級(jí)任務(wù),并移交CPU控制權(quán)給這個(gè)任務(wù)。因此,只需測(cè)量函數(shù)void OS_Sched()的執(zhí)行時(shí)間,再加上上下文切換時(shí)間就是搶占時(shí)間。在void OS_Sched()的開始和結(jié)束處,分別調(diào)用void cp_time_prof_start()和void cp_time_prof_end(),得到void OS_Sched()的執(zhí)行時(shí)間0.91μs,加上任務(wù)切換時(shí)間得到搶占時(shí)間1.66μs。
(3)信號(hào)洗牌時(shí)間:指從一個(gè)任務(wù)釋放信號(hào)量到另一個(gè)等待該信號(hào)的任務(wù)被激活的時(shí)間延遲。μC/OS-II是通過(guò)INT8U OSMutexPost()來(lái)釋放信號(hào)量的。在 INT8U OSMutexPost()的開始和結(jié)束處,分別調(diào)用void cp_time_prof_start()和 void cp_time_prof_end()。編寫兩個(gè)任務(wù),高優(yōu)先級(jí)的那個(gè)任務(wù)等待低優(yōu)先級(jí)任務(wù)釋放信號(hào)量。INT8U OSMutexPost()的執(zhí)行時(shí)間是2.38μs,加上任務(wù)切換時(shí)間,得出信號(hào)洗牌時(shí)間是3.13μs。
提出了一種基于μC/OS-II實(shí)時(shí)操作系統(tǒng)的代碼執(zhí)行時(shí)間測(cè)量方法,并用它測(cè)量了μC/OS-II實(shí)時(shí)操作系統(tǒng)在SmartFusion+SoftConsole平臺(tái)上的一些時(shí)間指標(biāo)。用這個(gè)方法,可以比較準(zhǔn)確地測(cè)量代碼執(zhí)行的時(shí)間,在代碼優(yōu)化階段可以用它來(lái)評(píng)估代碼優(yōu)化的實(shí)際效果,具有很強(qiáng)的實(shí)用意義。
[1]魏忠.嵌入式開發(fā)詳解[M].北京:電子工業(yè)出版,2003.
[2]Labrosse J J.μC/OS-II——源碼公開的實(shí)時(shí)嵌入式操作系統(tǒng)[M].邵貝貝,譯.北京:中國(guó)電力出版社,2001.
[3]Yiu J.ARM Cortex-M3權(quán)威指南[M].宋巖,譯.北京:北京航空航天大學(xué)出版社,2009.
[4]中興通訊股份有限公司.程序片斷執(zhí)行時(shí)間的測(cè)量方法及裝置:中國(guó),200910167583.6[P].2010-01-27.
[5]李江,戴勝華.Linux操作系統(tǒng)實(shí)時(shí)性測(cè)試及分析[J].計(jì)算機(jī)應(yīng)用,2005,25(7):1679-1681.
[6]唐建國(guó),張鐘澍,吳欽章.Linux下代碼運(yùn)行時(shí)間的高精度測(cè)量[J].計(jì)算機(jī)工程與應(yīng)用,2005,41(18):103-105.
[7]江建慧,唐智杰.測(cè)量嵌入式操作系統(tǒng)實(shí)時(shí)性能參數(shù)的新方法[J].同濟(jì)大學(xué)學(xué)報(bào),2008,36(9):1260-1266.
[8]王挺,丁志剛,閻夢(mèng)天,等.實(shí)時(shí)操作系統(tǒng)內(nèi)核時(shí)間參數(shù)的測(cè)量[J].計(jì)算機(jī)應(yīng)用與軟件,2009,26(7):59-61.