王浩波,王宜懷,劉長(zhǎng)勇,劉中華
(1.蘇州大學(xué) 計(jì)算機(jī)科學(xué)與技術(shù)學(xué)院,江蘇 蘇州 215006; 2.武夷學(xué)院 數(shù)學(xué)與計(jì)算機(jī)學(xué)院,福建 武夷山 354300)
實(shí)時(shí)操作系統(tǒng)RTOS[1](real time operation system)是一種具備較高實(shí)時(shí)性及穩(wěn)定性的嵌入式系統(tǒng)。一個(gè)復(fù)雜的嵌入式系統(tǒng)整體功能可以在RTOS的支持下,通過(guò)任務(wù)間通信的方式保證各個(gè)任務(wù)間能協(xié)調(diào)有序、互不沖突地同步運(yùn)行。線程信號(hào)作為RTOS的同步與通信方式中最基本的一種,用來(lái)通知線程發(fā)生了異步事件,進(jìn)而實(shí)現(xiàn)線程間消息傳遞。截至目前,也有少數(shù)學(xué)者在線程信號(hào)響應(yīng)機(jī)制原理方面展開(kāi)研究。例如,uClinux實(shí)時(shí)操作系統(tǒng)是通過(guò)信號(hào)屏蔽字的方式實(shí)現(xiàn)任務(wù)間協(xié)同管理與控制[2]。而Vxworks的任務(wù)間同步與通信是在軟件信號(hào)[3]設(shè)立的基礎(chǔ)上完成。eCOS則是利用條件變量來(lái)完成任務(wù)間的通信[4]。
但是,這些研究?jī)H針對(duì)線程信號(hào)的調(diào)用算法或應(yīng)用展開(kāi)研究,而對(duì)RTOS內(nèi)部的線程信號(hào)響應(yīng)原理、響應(yīng)時(shí)間性能或運(yùn)行流程并未介紹,不足以讓讀者深刻理解同步與通信原理機(jī)制。為此,通過(guò)對(duì)RTOS的線程信號(hào)通信原理、響應(yīng)流程以及其它關(guān)鍵技術(shù)要點(diǎn)的分析,在此基礎(chǔ)上提出基于printf方法輸出線程信號(hào)響應(yīng)機(jī)制流程的剖析方法,以STM32L431芯片為例,在mbedOS測(cè)試工程中不僅給出了printf輸出語(yǔ)句的加入位置及方法,同時(shí)也對(duì)線程信號(hào)內(nèi)部響應(yīng)機(jī)制流程及時(shí)間進(jìn)行了系統(tǒng)分析[5]。結(jié)果分析表明,將線程信號(hào)內(nèi)部整個(gè)響應(yīng)機(jī)制流程信息通過(guò)printf方法輸出顯示在PC機(jī)中,不僅有助于讀者從更加微觀的層面來(lái)理解同步與通信原理機(jī)制,也為深入理解或設(shè)計(jì)新的RTOS提供了重要的理論研究基礎(chǔ)。
在RTOS的同步與通信機(jī)制中,相比于通過(guò)事件字來(lái)表達(dá)多種可能的邏輯結(jié)果,線程信號(hào)是最簡(jiǎn)單的同步手段,常應(yīng)用于協(xié)調(diào)任務(wù)間或中斷與任務(wù)間的通信、僅表達(dá)某一具體的情況而無(wú)需傳遞數(shù)據(jù)的場(chǎng)景。
線程信號(hào)作為線程間通信的重要媒介之一,用來(lái)通知線程發(fā)生了異步事件,操作系統(tǒng)內(nèi)核也可以因?yàn)閮?nèi)部事件而給線程發(fā)送信號(hào),通知線程發(fā)生了某個(gè)事件,在mbedOS中采用線程信號(hào)、eCOS中采用條件變量、uClinux中采用信號(hào)屏蔽字、Vxworks中采用軟件信號(hào)的方式來(lái)實(shí)現(xiàn)。
若兩個(gè)任務(wù)間的同步與通信是通過(guò)線程信號(hào)的方式來(lái)實(shí)現(xiàn),則其中必定會(huì)有一個(gè)任務(wù)通過(guò)某種方式在等待該線程信號(hào),同時(shí)另外一個(gè)任務(wù)則采用某種方式或函數(shù)設(shè)置具體的線程信號(hào)值,例如在mbedOS中,線程調(diào)用signal_wait函數(shù)等待一個(gè)線程信號(hào),實(shí)際上是將所要等待的線程信號(hào)值記錄在線程控制塊的waitFlags變量中;在uClinux實(shí)時(shí)操作系統(tǒng)中,等待信號(hào)的產(chǎn)生由sigwait函數(shù)完成;Vxworks則是在程序執(zhí)行到sig_suspend這一函數(shù)處,系統(tǒng)掛起當(dāng)前任務(wù)并等待具體的軟件信號(hào)值;而eCOS在等待條件變量前會(huì)先調(diào)用cyg_cond_wait這一函數(shù)。這個(gè)時(shí)候任務(wù)狀態(tài)由就緒態(tài)切換為阻塞態(tài),并從就緒隊(duì)列移出再放到等待隊(duì)列中。之后會(huì)在另一任務(wù)設(shè)置線程信號(hào)值,如mbedOS通過(guò)調(diào)用線程設(shè)置函數(shù)signal_set設(shè)置線程信號(hào)值,實(shí)際是將要設(shè)置的具體線程信號(hào)值記錄在線程控制塊的threadFlags變量中;uClinux向特定的任務(wù)發(fā)送信號(hào)前會(huì)先調(diào)用signal函數(shù);Vxworks在kill函數(shù)的作用下,向特定的任務(wù)發(fā)送具體的信號(hào)值;而eCOS喚醒所有等待條件變量前,會(huì)事先調(diào)用cyg_cond_broadcast或cyg_cond_signal函數(shù),可分別喚醒多個(gè)或一個(gè)等待該條件變量的線程。之后它們會(huì)在各自的線程信號(hào)設(shè)置處判斷其中是否有等待具體信號(hào)值的任務(wù)或線程,若有則將其狀態(tài)由阻塞態(tài)切換為就緒態(tài),并從等待隊(duì)列移出再放到就緒隊(duì)列中。4種操作系統(tǒng)的線程信號(hào)調(diào)度對(duì)比過(guò)程如圖1所示。
圖1 4種操作系統(tǒng)的線程信號(hào)調(diào)度對(duì)比過(guò)程
在線程信號(hào)響應(yīng)機(jī)制的過(guò)程中,會(huì)涉及到線程信號(hào)的等待、線程信號(hào)的產(chǎn)生、線程信號(hào)的存儲(chǔ)、線程信號(hào)的識(shí)別、線程信號(hào)的響應(yīng)以及線程信號(hào)的調(diào)度等關(guān)鍵技術(shù)要點(diǎn)。
(1)線程信號(hào)的等待:其定義請(qǐng)參見(jiàn)文獻(xiàn)[6];
(2)線程信號(hào)的產(chǎn)生:即指明線程信號(hào)由誰(shuí)負(fù)責(zé)設(shè)置,在什么時(shí)間以及什么位置被設(shè)置,設(shè)置的線程信號(hào)值是多少;
(3)線程信號(hào)的存儲(chǔ):即線程信號(hào)被設(shè)置完成后,指明線程信號(hào)值存儲(chǔ)的時(shí)間和存儲(chǔ)位置;
(4)線程信號(hào)的識(shí)別:線程信號(hào)被存儲(chǔ)后,指明線程信號(hào)被識(shí)別的時(shí)間和識(shí)別對(duì)象;
(5)線程信號(hào)的響應(yīng):其定義請(qǐng)參見(jiàn)文獻(xiàn)[6];
(6)線程信號(hào)的調(diào)度:其定義請(qǐng)參見(jiàn)文獻(xiàn)[6]。
若任務(wù)間的同步與通信機(jī)制是通過(guò)線程信號(hào)的方式來(lái)實(shí)現(xiàn),則其內(nèi)部等待和設(shè)置線程信號(hào)的過(guò)程通常是隱藏在內(nèi)核程序中,通過(guò)printf措施將線程信號(hào)內(nèi)部響應(yīng)的中間流程信息直觀地顯示在PC機(jī)中并加以剖析,可以幫助讀者捋清線程信號(hào)響應(yīng)的整個(gè)脈絡(luò)結(jié)構(gòu),加深對(duì)該同步與通信方式的理解。
要理解線程信號(hào)響應(yīng)機(jī)制的過(guò)程,可以通過(guò)以下3種方法。第一種是直接查看源程序的方法,直接根據(jù)程序代碼含義來(lái)分析整個(gè)程序的執(zhí)行流程,該方法對(duì)于無(wú)操作系統(tǒng)程序或單一程序的分析是可行的[6]。但對(duì)RTOS而言,程序的執(zhí)行是非靜態(tài)且并非一次性運(yùn)行完成,而是由RTOS根據(jù)調(diào)度策略(mbedOS中采用優(yōu)先級(jí)搶占以及時(shí)間片輪詢調(diào)度策略)對(duì)任務(wù)進(jìn)行動(dòng)態(tài)調(diào)度。因此,直接分析源程序這種靜態(tài)的方式很難模擬出RTOS輪詢調(diào)度的動(dòng)態(tài)過(guò)程[7]。第二種是通過(guò)斷點(diǎn)調(diào)試手段,即在需要調(diào)試的語(yǔ)句上進(jìn)行斷點(diǎn)設(shè)置,通過(guò)單步跟蹤的方式逐語(yǔ)句分析線程信號(hào)響應(yīng)流程。但在有RTOS的情況下,其內(nèi)部會(huì)存在一個(gè)定時(shí)器中斷,該中斷會(huì)在每個(gè)時(shí)間嘀嗒到來(lái)時(shí)自動(dòng)運(yùn)行,若采取斷點(diǎn)調(diào)試的措施,則無(wú)法進(jìn)入到定時(shí)器中斷內(nèi)部進(jìn)行語(yǔ)句分析,也就無(wú)法分析線程信號(hào)的響應(yīng)機(jī)制。還有一種是printf輸出方法,printf調(diào)試方法[8]作為動(dòng)態(tài)分析的最原始形式之一,被廣泛應(yīng)用在各個(gè)領(lǐng)域中。例如:在CPU仿真技術(shù)中通過(guò)printf方法可以打印輸出調(diào)試信息;在計(jì)算機(jī)視覺(jué)應(yīng)用中通過(guò)printf調(diào)試方法將中間過(guò)程信息打印輸出;還可以在程序功能分析過(guò)程中采用printf調(diào)試方法輸出程序錯(cuò)誤信息。因此,借鑒類PC機(jī)的printf調(diào)試方法,不但能夠?qū)崿F(xiàn)將線程信號(hào)響應(yīng)過(guò)程的各種狀態(tài)動(dòng)態(tài)地輸出顯示在PC機(jī)中,也有助于更好地剖析線程信號(hào)響應(yīng)機(jī)制的內(nèi)部過(guò)程。
為更好地剖析線程信號(hào)響應(yīng)機(jī)制過(guò)程,可以考慮從以下兩個(gè)方面插入相應(yīng)的printf語(yǔ)句輸出過(guò)程信息以及響應(yīng)時(shí)間。
線程信號(hào)整個(gè)響應(yīng)流程信息輸出注重的是任務(wù)狀態(tài)的變化、任務(wù)處于哪個(gè)隊(duì)列、任務(wù)是怎樣進(jìn)出隊(duì)列的、等待的線程信號(hào)是否發(fā)生改變以及在哪里被設(shè)置。因此,printf語(yǔ)句應(yīng)該插入在調(diào)用線程信號(hào)等待、設(shè)置以及任務(wù)進(jìn)出各種列表函數(shù)的前后等位置,這樣可以輸出各種狀態(tài)信息。
在剖析RTOS線程信號(hào)機(jī)制的響應(yīng)時(shí)間時(shí),應(yīng)著重考慮線程信號(hào)響應(yīng)的快慢和耗費(fèi)的時(shí)間長(zhǎng)短。在這一過(guò)程中,printf語(yǔ)句的執(zhí)行也需花費(fèi)一定的執(zhí)行時(shí)間:由于主控芯片STM32L431采用的是48 MHz的內(nèi)核時(shí)鐘頻率,故通過(guò)printf調(diào)試手段打印輸出一個(gè)字符約需77 μs,由此可算出所有printf執(zhí)行的時(shí)間。按照無(wú)損檢測(cè)[9]的要求規(guī)定:待檢測(cè)對(duì)象性能的完整性不可以受到所選用的檢驗(yàn)技術(shù)及檢測(cè)設(shè)備的干擾。因此,此處可以通過(guò)代碼插樁的方法來(lái)測(cè)試響應(yīng)時(shí)間性能,即采用變量記錄響應(yīng)時(shí)間并在任務(wù)獲得響應(yīng)時(shí)才利用printf方式輸出顯示時(shí)間信息,而不是在源程序中所有位置都插入printf語(yǔ)句,通過(guò)這種方式才可以比較準(zhǔn)確地計(jì)算出線程信號(hào)響應(yīng)整個(gè)過(guò)程的時(shí)間。將任務(wù)執(zhí)行到線程信號(hào)等待語(yǔ)句signal_wait處的時(shí)刻設(shè)置為T(mén)0, 標(biāo)志線程信號(hào)等待開(kāi)始。將任務(wù)執(zhí)行到線程信號(hào)值設(shè)置語(yǔ)句signal_set處的時(shí)刻設(shè)置為T(mén)1, 標(biāo)志線程信號(hào)的產(chǎn)生。將任務(wù)執(zhí)行到線程信號(hào)存儲(chǔ)語(yǔ)句處的時(shí)刻設(shè)置為T(mén)2, 標(biāo)志該線程信號(hào)進(jìn)入存儲(chǔ)待識(shí)別階段。將任務(wù)執(zhí)行到線程信號(hào)被識(shí)別語(yǔ)句處的時(shí)刻設(shè)置為T(mén)3, 標(biāo)志該線程信號(hào)進(jìn)入到被識(shí)別階段。將任務(wù)執(zhí)行到線程信號(hào)由被識(shí)別到進(jìn)入就緒隊(duì)列語(yǔ)句處的時(shí)刻設(shè)置為T(mén)4。 最后將任務(wù)執(zhí)行到由就緒隊(duì)列取出到被RTOS調(diào)度語(yǔ)句處的時(shí)刻設(shè)置為T(mén)5。 完成上述時(shí)間刻度設(shè)置之后,便可以計(jì)算出線程信號(hào)在各個(gè)周期的時(shí)間差值:線程信號(hào)由產(chǎn)生到存儲(chǔ)為 (T2-T1)、 由存儲(chǔ)到被識(shí)別為 (T3-T2)、 由識(shí)別再到響應(yīng)為 (T4-T3) 以及由等待線程信號(hào)到再次被mbedOS[10]調(diào)度執(zhí)行的時(shí)間間隔為 (T5-T0)。
mbedOS是ARM公司在2014年推出的一款免費(fèi)的、跨平臺(tái)的、專為物聯(lián)網(wǎng)(IoT)中的“事物”而設(shè)計(jì)的輕量級(jí)開(kāi)源嵌入式操作系統(tǒng),不僅具備一般RTOS的基本功能,開(kāi)發(fā)設(shè)計(jì)人員還可以在低成本開(kāi)發(fā)板上快速構(gòu)建IoT應(yīng)用原型[11],被廣泛應(yīng)用在物聯(lián)網(wǎng)設(shè)備平臺(tái)[12]以及通信與安全訪問(wèn)服務(wù)機(jī)制[13]等領(lǐng)域。下面將通過(guò)實(shí)踐分析mbedOS的線程信號(hào)響應(yīng)機(jī)制。
對(duì)線程信號(hào)響應(yīng)機(jī)制的分析,主要在于要選擇好printf語(yǔ)句的插入位置,這樣才能輸出響應(yīng)過(guò)程的關(guān)鍵信息。
(1)在線程信號(hào)等待語(yǔ)句“Thread::signal_wait(GREEN_SIGNAL)”前后位置加入相應(yīng)的printf語(yǔ)句,用來(lái)顯示線程在等待線程信號(hào)前和獲得線程信號(hào)后的關(guān)鍵信息,在該語(yǔ)句之后用T5記錄線程被調(diào)度的時(shí)刻;在線程信號(hào)設(shè)置語(yǔ)句“thd_greenlight.signal_set(GREEN_SIGNAL)”的前后位置加入相應(yīng)的printf輸出信息,則可以顯示設(shè)置線程信號(hào)值前后的狀態(tài)變化信息,并在該語(yǔ)句之前用T1記錄線程信號(hào)產(chǎn)生的時(shí)刻。
(2)在“thread->waitFlags=flag”前后位置加入相應(yīng)的printf語(yǔ)句,用來(lái)輸出當(dāng)前線程信號(hào)等待標(biāo)志waitFlags值的變化情況,并在“osRtxThreadDelayInsert(thread, timeout)”語(yǔ)句之前用T0記錄線程開(kāi)始等待線程信號(hào)時(shí)刻;然后在“threadFlags = threadFlagSet(thread,flag)”這一語(yǔ)句后面加入對(duì)應(yīng)的printf輸出語(yǔ)句,并用T2記錄線程信號(hào)存儲(chǔ)的時(shí)刻。
(3)在延時(shí)等待隊(duì)列移除線程函數(shù)osRtxThreadDelayRemove之前可用T3記錄識(shí)別到等待線程信號(hào)的具體線程的時(shí)刻;在線程進(jìn)隊(duì)函數(shù)osRtxThreadReadyPut之后可用T4記錄將等待線程信號(hào)的線程放入到就緒隊(duì)列的時(shí)刻。
(4)除此之外,還可以在線程移出函數(shù)osRtxThreadWaitExit、線程延時(shí)等待函數(shù)osRtxThreadWaitEnter等前后位置加入對(duì)應(yīng)的printf輸出信息,不僅可以直觀地跟蹤程序運(yùn)行狀態(tài),還可以顯示線程進(jìn)出各種隊(duì)列的變化情況。
mbedOS作為一款廣泛應(yīng)用于物聯(lián)網(wǎng)平臺(tái)的實(shí)時(shí)操作系統(tǒng),其內(nèi)部通過(guò)兩種機(jī)制來(lái)設(shè)置線程信號(hào):第一種機(jī)制是在某個(gè)線程中等待線程信號(hào),在另一線程中通過(guò)觸發(fā)SVC中斷來(lái)設(shè)置線程信號(hào);第二種機(jī)制是在某個(gè)線程中等待線程信號(hào),而設(shè)置具體的線程信號(hào)值則是通過(guò)PendSV中斷機(jī)制來(lái)實(shí)現(xiàn)。為準(zhǔn)確測(cè)試出線程信號(hào)的響應(yīng)時(shí)間,可以在mbedOS測(cè)試樣例工程中只設(shè)定包含兩個(gè)優(yōu)先級(jí)相同的線程,即藍(lán)燈線程和綠燈線程,并運(yùn)行在STM32L431微控制器上。mbedOS內(nèi)部的定時(shí)器中斷SysTick是通過(guò)時(shí)間嘀嗒的方式來(lái)產(chǎn)生中斷,即每個(gè)時(shí)間嘀嗒(1 ms)可以產(chǎn)生一次中斷,對(duì)應(yīng)SysTick中的計(jì)數(shù)器需要計(jì)數(shù)48 000次(STM32L431芯片內(nèi)核時(shí)鐘頻率是48 MHz,計(jì)數(shù)器采用減1的方式進(jìn)行計(jì)數(shù));而在線程的優(yōu)先級(jí)相同的情況下,mbedOS是利用時(shí)間片輪詢調(diào)度策略來(lái)選擇某個(gè)線程進(jìn)行調(diào)度,即每個(gè)時(shí)間片大小為5個(gè)時(shí)間嘀嗒。實(shí)時(shí)性能的軟件測(cè)試[14]作為一個(gè)設(shè)計(jì)和考核實(shí)時(shí)操作系統(tǒng)性能的重要方法之一,主要是通過(guò)系統(tǒng)調(diào)用的方式來(lái)獲得操作系統(tǒng)內(nèi)核中任意兩處代碼執(zhí)行的時(shí)間間隔。本文采用SVC中斷的方式來(lái)分析線程信號(hào)的響應(yīng)時(shí)間,主要有:線程信號(hào)由產(chǎn)生到存儲(chǔ) (T2-T1)、 由存儲(chǔ)到被識(shí)別 (T3-T2)、 由識(shí)別再到響應(yīng) (T4-T3) 以及由等待線程信號(hào)到再次被mbedOS調(diào)度執(zhí)行的時(shí)間間隔 (T5-T0)。 測(cè)試方法是在時(shí)間嘀嗒中斷服務(wù)例程SysTick_Handler函數(shù)中將全局變量TimerCount(用來(lái)記錄SysTick中斷次數(shù))加1,即先取得TimerCount變量存放的絕對(duì)地址,然后給該地址存放的內(nèi)容執(zhí)行加1操作再重新賦值給該地址,通過(guò)比較不同時(shí)刻的Systick中斷計(jì)數(shù)值VAL以及中斷次數(shù)SUM(由TimerCount變量值的大小決定),采用Δt公式計(jì)算求得實(shí)際執(zhí)行時(shí)間,即線程信號(hào)在某個(gè)執(zhí)行過(guò)程需耗費(fèi)實(shí)際時(shí)間的計(jì)算公式為: Δt=Tend-Tbegin=((VALbegin-VALend)+48000*(SUMend-SUMbegin))/48(μs)。 表1給出了基于SVC中斷的線程信號(hào)響應(yīng)時(shí)間分析情況,其中 (T2-T1)、 (T3-T2)、 (T4-T3) 的響應(yīng)時(shí)間都較短,在8 μs之內(nèi),說(shuō)明線程信號(hào)作為同步與通信手段能滿足實(shí)時(shí)性的要求。而綠燈線程由等待線程信號(hào)到再次被調(diào)度執(zhí)行的時(shí)間都在5 ms~6 ms之內(nèi),則可以說(shuō)明在線程優(yōu)先級(jí)相同的情況下,mbedOS實(shí)時(shí)操作系統(tǒng)是采用時(shí)間片輪詢的方式來(lái)對(duì)線程進(jìn)行調(diào)度,響應(yīng)時(shí)間同樣也比較快,實(shí)時(shí)性要求能得到滿足。
表1 基于SVC中斷的線程信號(hào)響應(yīng)時(shí)間分析
基于篇幅的有限性,這里僅給出測(cè)試線程信號(hào)由產(chǎn)生到存儲(chǔ) (T2-T1) 過(guò)程中的關(guān)鍵代碼,其余各時(shí)間段的測(cè)試流程分析與此類似。
(1)在時(shí)間嘀嗒中斷服務(wù)例程處理函數(shù)SysTick_Handler中設(shè)置SysTick中斷次數(shù)累加的關(guān)鍵代碼如下:
SysTick_Handler:
//棧指針(MSP或PSP)和LR(EXC_RETURN)入棧
PUSH {R0,LR}
ldr r2,=0x20001884
ldr r3,[r2]
adds r3,r3,#1
str r3,[r2]
//調(diào)用SysTick中斷服務(wù)例程osRtxTick_Handler
BL osRtxTick_Handler
//R0←堆棧中保存的R0;
//LR←堆棧中保存的LR(EXC_RETURN)
POP {R0,LR}
代碼段分析:其中0x20001884是全局變量TimerCount(用來(lái)記錄SysTick中斷次數(shù))編譯后的地址,可通過(guò)編譯后生成的Debug目錄文件下的.map文件找到該地址,先將該地址存放的數(shù)據(jù)賦值給寄存器r3,再將自加1后的數(shù)據(jù)存放回原地址即可實(shí)現(xiàn)每次觸發(fā)SysTick中斷時(shí)次數(shù)累加1。
(2)在藍(lán)燈線程調(diào)用signal_set函數(shù)之前記為T(mén)1時(shí)刻的關(guān)鍵代碼:
//T1時(shí)刻
n0=TimerCount;
v0=SysTick->VAL;
//設(shè)置綠燈信號(hào)
thd_greenlight.signal_set(GREEN_SIGNAL);
代碼段分析:n0用來(lái)記錄T1時(shí)刻觸發(fā)的SysTick中斷次數(shù);v0則記錄T1時(shí)刻Systick的計(jì)數(shù)值VAL, GREEN_SIGNAL是綠燈等待的線程信號(hào)值,大小為0x47。這個(gè)值的大小可以在宏定義處自己設(shè)定。
(3)在SVC實(shí)際處理線程信號(hào)設(shè)置函數(shù)svcRtxThreadFlagSet中,線程信號(hào)值被賦值給threadFlags變量之后記為T(mén)2時(shí)刻的關(guān)鍵代碼:
//(2)設(shè)置線程信號(hào)值
threadFlags = ThreadFlagSet(thread, flag);
//T2時(shí)刻
n5=TimerCount;
v5=SysTick->VAL;
if(v5>v0)
printf("%d ",v0-v5+(n5-n0)*48000);
else
printf("%d ",v0-v5);
代碼段分析:調(diào)用ThreadFlagSet函數(shù)將對(duì)應(yīng)線程(綠燈線程)的線程信號(hào)值設(shè)置為0x47,并存儲(chǔ)在threadFlags變量中,n5用來(lái)記錄T2時(shí)刻觸發(fā)的SysTick中斷次數(shù);v5則記錄T2時(shí)刻Systick的計(jì)數(shù)值VAL。
mbedOS線程信號(hào)響應(yīng)機(jī)制的測(cè)試樣例程序是基于Cortex-M4F內(nèi)核的STM32L431微控制器和STM32CubeIDE 1.0.0集成開(kāi)發(fā)環(huán)境進(jìn)行的。STM32L431采用的是256 KB的片內(nèi)Flash和64 KB的靜態(tài)隨機(jī)存儲(chǔ)器SRAM,F(xiàn)lash主要用于程序代碼及中斷向量表的存儲(chǔ),而片內(nèi)RAM主要用于各類變量的存儲(chǔ)[15]等。芯片采用LQFP-64封裝,不僅具有豐富的功能模塊:包括GPIO引腳、ADC模塊、PWM定時(shí)器等,還包含具備強(qiáng)大運(yùn)算能力的浮點(diǎn)運(yùn)算單元FPU。STM32L431微控制器不僅包括ARM Cortex-M4F內(nèi)核以及各種外設(shè)、存儲(chǔ)器模塊,還包含了高性能系統(tǒng)和外設(shè)兩種總線。另外,STM32L431微控制器還提供可擴(kuò)展總線用來(lái)連接其它外圍設(shè)備,其結(jié)構(gòu)如圖2所示。
圖2 STM32L431微控制器結(jié)構(gòu)
本文用到的測(cè)試工程的功能主要是在mbedOS啟動(dòng)后通過(guò)主線程來(lái)創(chuàng)建3個(gè)相同優(yōu)先級(jí)的用戶線程,分別是藍(lán)燈線程、綠燈線程以及紅燈線程。然后在綠燈線程中調(diào)用signal_wait函數(shù)等待綠燈線程信號(hào)(GREEN_SIGNAL),從而實(shí)現(xiàn)綠燈的亮暗切換;而藍(lán)燈線程則是通過(guò)調(diào)用signal_wait函數(shù)來(lái)等待藍(lán)燈線程信號(hào)(BLUE_SIGNAL),進(jìn)而實(shí)現(xiàn)藍(lán)燈的亮暗切換;在紅燈線程中通過(guò)調(diào)用signal_set函數(shù)分別設(shè)置綠燈線程和藍(lán)燈線程的線程等待信號(hào)。圖3給出了mbedOS測(cè)試樣例程序的執(zhí)行流程。
圖3 mbedOS測(cè)試樣例程序的執(zhí)行流程
在測(cè)試程序中,當(dāng)程序執(zhí)行到“Thread::signal_wait(GREEN_SIGNAL)”語(yǔ)句時(shí),則表示此時(shí)綠燈線程需要等待綠燈線程信號(hào)(GREEN_SIGNAL),綠燈線程阻塞被放入到等待隊(duì)列中。當(dāng)測(cè)試程序執(zhí)行到“thd_greenlight.signal_set(GREEN_SIGNAL)”語(yǔ)句時(shí),則相當(dāng)于此時(shí)向綠燈線程發(fā)送了一個(gè)線程信號(hào),綠燈線程收到該信號(hào)后,會(huì)將之前阻塞的綠燈線程從等待隊(duì)列移出并放入到就緒隊(duì)列中,由mbedOS通過(guò)時(shí)間片輪詢的調(diào)度策略,將線程狀態(tài)由阻塞態(tài)更改為激活態(tài),并進(jìn)行上下文切換,然后執(zhí)行后續(xù)的語(yǔ)句,即執(zhí)行g(shù)pio_reverse(LIGHT_GREEN)語(yǔ)句實(shí)現(xiàn)切換綠燈亮暗。藍(lán)燈線程的運(yùn)行流程與綠燈線程一樣。
在STM32L431微控制器的基礎(chǔ)上,以測(cè)量有關(guān)的確定性時(shí)序分析[16]方法為例。即在mbedOS測(cè)試工程中的對(duì)應(yīng)語(yǔ)句前后位置插入printf來(lái)輸出信息,圖4給出了基于線程信號(hào)工作原理的線程調(diào)度過(guò)程時(shí)序圖。
結(jié)合前面內(nèi)容分析,可以將線程信號(hào)響應(yīng)的流程分析大致分為以下9個(gè)步驟,基于篇幅局限性的原因,本文僅給出與綠燈線程相關(guān)的printf輸出信息。其中,藍(lán)燈線程的地址是20001774,綠燈線程的地址是20001834,紅燈線程的地址是200016B4,定時(shí)器線程的地址是2000136C,空閑線程地址是20001328,缺省處理函數(shù)DefaultISR的地址是8001A21。而地址200001E4表示等待隊(duì)列,地址200001D0表示就緒隊(duì)列。
(1)啟動(dòng)線程
從芯片上電到操作系統(tǒng)的啟動(dòng),其中包括上電復(fù)位及mbedOS啟動(dòng)兩大部分,在mbedOS啟動(dòng)后,測(cè)試程序會(huì)先執(zhí)行對(duì)應(yīng)的主線程函數(shù)app_init,由該函數(shù)依次創(chuàng)建紅燈、綠燈和藍(lán)燈3個(gè)用戶線程,接著啟動(dòng)這3個(gè)用戶線程,然后主線程被阻塞[17]。此時(shí),CPU的控制權(quán)交由mbedOS內(nèi)核,開(kāi)始對(duì)用戶線程的調(diào)度,即取出并激活就緒隊(duì)列中優(yōu)先級(jí)最高的紅燈線程運(yùn)行。
(2)綠燈線程等待綠燈信號(hào)
mbedOS內(nèi)部的SysTick中斷會(huì)每隔1 ms中斷一次并按照每個(gè)時(shí)間片(5 ms)為周期的方式對(duì)線程進(jìn)行輪詢調(diào)度,此時(shí)綠燈線程被激活,并通過(guò)執(zhí)行“Thread::signal_wait(GREEN_SIGNAL)”語(yǔ)句來(lái)等待綠燈信號(hào)GREEN_SIGNAL(0x47),在執(zhí)行該語(yǔ)句的過(guò)程中,由于綠燈信號(hào)此時(shí)還未產(chǎn)生,綠燈線程狀態(tài)會(huì)從激活態(tài)轉(zhuǎn)換成為阻塞態(tài),并移入等待隊(duì)列中,同時(shí),從就緒隊(duì)列中取出優(yōu)先級(jí)最高的紅燈線程激活運(yùn)行。
printf輸出結(jié)果如下:
2.當(dāng)前運(yùn)行的線程=20001834(綠燈)開(kāi)始。
2-1.當(dāng)前運(yùn)行的線程=20001834(綠燈)調(diào)用signal_wait(GREEN_SIGNAL)等待線程信號(hào)0x47。
2-1-1.設(shè)置前當(dāng)前運(yùn)行線程=20001834的等待標(biāo)識(shí)=0
5-1.調(diào)用osRtxThreadWaitEnter前等待隊(duì)列=200001E4中的線程: 2000136C->0->8001A21
5-2.調(diào)用osRtxThreadWaitEnter前就緒隊(duì)列=200001D0中的線程: 20001774->200016B4->20001328
6-1.調(diào)用osRtxThreadWaitEnter->osRtxThreadDelayInsert將當(dāng)前運(yùn)行線程=20001834放到等待隊(duì)列
6-2.調(diào)用osRtxThreadWaitEnter->osRtxThreadListGet從就緒隊(duì)列獲取優(yōu)先級(jí)最高的線程=20001774
6-3.調(diào)用osRtxThreadWaitEnter->osRtxThreadSwitch將線程=20001774設(shè)置為激活態(tài)準(zhǔn)備運(yùn)行
7-1.調(diào)用osRtxThreadWaitEnter后等待隊(duì)列:2000136C->20001834->0
7-2.調(diào)用osRtxThreadWaitEnter后就緒隊(duì)列: 200016B4->20001328->0
(3)藍(lán)燈線程等待藍(lán)燈信號(hào)
藍(lán)燈線程通過(guò)執(zhí)行“Thread::signal_wait(BLUE_SIGNAL)”語(yǔ)句來(lái)等待藍(lán)燈信號(hào)BLUE_SIGNAL(0x42),在執(zhí)行該語(yǔ)句的過(guò)程中藍(lán)燈線程狀態(tài)由激活態(tài)更改為阻塞態(tài),放入到等待隊(duì)列中,然后取出就緒隊(duì)列中優(yōu)先級(jí)最高的紅燈線程,并將其激活運(yùn)行。
(4)紅燈線程設(shè)置綠燈信號(hào)
在紅燈線程中通過(guò)調(diào)用“thd_greenlight。signal_set(GREEN_SIGNAL)”函數(shù)來(lái)設(shè)置綠燈信號(hào),此時(shí)mbedOS會(huì)將綠燈線程由等待隊(duì)列移出并將其放入到就緒隊(duì)列中?;诰€程優(yōu)先級(jí)相同的原因,此時(shí)綠燈線程并不會(huì)搶占當(dāng)前正在運(yùn)行的藍(lán)燈線程,而是會(huì)在SysTick中斷中通過(guò)輪詢調(diào)度的方式將綠燈線程激活運(yùn)行。
printf輸出結(jié)果如下:
1-1.當(dāng)前運(yùn)行的線程=200016B4 (紅燈),調(diào)用signal_set(GREEN_SIGNAL)設(shè)置綠燈等待線程信號(hào)0x47。
1-1-1.線程信號(hào)設(shè)置前的值=0
1-1-2.線程信號(hào)設(shè)置后的值=47
8-1.調(diào)用osRtxThreadWaitExit前等待隊(duì)列=200001E4中的程:2000136C->20001834->20001774
8-2.調(diào)用osRtxThreadWaitExit前就緒隊(duì)列=200001D0中的線程:20001328->0->8001A21
9-1.調(diào)用osRtxThreadWaitExit->osRtxThreadDelayRemove將線程=20001834從等待隊(duì)列中移出
9-2.調(diào)用osRtxThreadWaitExit->osRtxThreadReadyPut->osRtxThreadListPut將線程=20001834放到放到就緒隊(duì)列
10-1.調(diào)用osRtxThreadWaitExit后等待隊(duì)列:2000136C->20001774->0
10-2.調(diào)用osRtxThreadWaitExit后就緒隊(duì)列=200001D0中的線程:20001834->20001328->0
11.調(diào)用signal_set()結(jié)束。
(5)輪詢調(diào)度激活綠燈線程
在藍(lán)燈線程中設(shè)置完綠燈信號(hào)后,mbedOS內(nèi)部機(jī)制的SysTick中斷會(huì)每隔1 ms中斷一次并按照每個(gè)時(shí)間片(5 ms)為周期的方式對(duì)線程進(jìn)行輪詢調(diào)度,此時(shí)將激活綠燈線程運(yùn)行。
printf輸出結(jié)果如下:
12.進(jìn)入SysTick_Handler觸發(fā)osRtxTick_Handler對(duì)優(yōu)先級(jí)相同的線程進(jìn)行輪詢調(diào)度,將線程=20001834切換為激活態(tài)。
(6)綠燈線程獲得綠燈信號(hào)
綠燈線程在獲得綠燈信號(hào)后,會(huì)從等待隊(duì)列移出進(jìn)入就緒隊(duì)列,然后被激活運(yùn)行,實(shí)現(xiàn)切換綠燈亮暗,即執(zhí)行“Thread::signal_wait(GREEN_SIGNAL)”后續(xù)的語(yǔ)句。然后綠燈線程開(kāi)始等待下一次的線程信號(hào)(即重復(fù)4-7步,如圖4所示),再激活紅燈線程運(yùn)行。
printf輸出結(jié)果如下:
2-2.當(dāng)前運(yùn)行的線程=20001834(綠燈)已等到線程信0x47,綠燈反轉(zhuǎn)。
2.當(dāng)前運(yùn)行的線程=20001834(綠燈)結(jié)束。
(7)紅燈線程設(shè)置藍(lán)燈信號(hào)
在紅燈線程中通過(guò)調(diào)用“thd_bluelight.signal_set(BLUE_SIGNAL)”函數(shù)來(lái)設(shè)置藍(lán)燈信號(hào),此時(shí)mbedOS會(huì)將藍(lán)燈線程從等待隊(duì)列移出,此時(shí)線程狀態(tài)會(huì)由阻塞態(tài)更換為就緒態(tài),并放入到就緒隊(duì)列中。由于mbedOS對(duì)優(yōu)先級(jí)相同的線程采用輪詢調(diào)度策略,此時(shí)藍(lán)燈線程并不會(huì)搶占當(dāng)前正在運(yùn)行的紅燈線程,而是會(huì)在SysTick中斷中通過(guò)輪詢調(diào)度的方式將藍(lán)燈線程激活運(yùn)行。
(8)輪詢調(diào)度激活藍(lán)燈線程
在紅燈線程中設(shè)置完藍(lán)燈信號(hào)后,mbedOS內(nèi)部機(jī)制的SysTick中斷會(huì)每隔1 ms中斷一次并按照每個(gè)時(shí)間片(5 ms)為周期的方式對(duì)線程進(jìn)行輪詢調(diào)度,此時(shí)將會(huì)激活藍(lán)燈線程運(yùn)行。
(9)藍(lán)燈線程獲得藍(lán)燈信號(hào)
藍(lán)燈線程獲得藍(lán)燈信號(hào)后執(zhí)行“Thread::signal_wait(BLUE_SIGNAL)”后續(xù)的語(yǔ)句,實(shí)現(xiàn)切換藍(lán)燈亮暗。然后藍(lán)燈線程開(kāi)始新一輪的等待信號(hào)(即重復(fù)9-13步,如圖4所示),再激活紅燈線程運(yùn)行。
本文闡述了mbedOS同步與通信手段——線程信號(hào)的響應(yīng)機(jī)制,并在STM32L431微控制器上進(jìn)行實(shí)驗(yàn)測(cè)試,通過(guò)printf調(diào)試手段將線程信號(hào)工作原理的整個(gè)調(diào)度過(guò)程信息輸出到PC機(jī)中,并在mbedOS測(cè)試工程中以時(shí)序圖的方式進(jìn)行分析總結(jié)。可以讓讀者更加透明地理解線程信號(hào)響應(yīng)的整個(gè)脈絡(luò)結(jié)構(gòu),不僅有助于從微觀的層面來(lái)理解RTOS的調(diào)度過(guò)程。同時(shí),也為進(jìn)一步剖析RTOS的其它同步與通信方式提供了方法借鑒。后續(xù)將進(jìn)一步對(duì)RTOS的其它同步與通信方式進(jìn)行理解與分析。