黃 睿
[摘要]討論源代碼開放的實(shí)時(shí)操作系統(tǒng)μC/OS-Ⅱ在目前流行的嵌入式微控制器ARM7上的移植,從非運(yùn)行態(tài)任務(wù)的堆棧一致性這個(gè)角度來聯(lián)通分析各個(gè)主要移植函數(shù)的編寫。通過分析能更透徹的理解任務(wù)堆棧結(jié)構(gòu)在操作系統(tǒng)移植中的重要性,對(duì)把操作系統(tǒng)移植到不同的處理器具有一定的參考價(jià)值。
[關(guān)鍵詞]移植堆棧結(jié)構(gòu) μC/OS-Ⅱ ARM7
中圖分類號(hào):TP3文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào):1671-7597(2009)0920066-02
一、引言
在微處理器上引入操作系統(tǒng)代替?zhèn)鹘y(tǒng)的單片機(jī)前后臺(tái)系統(tǒng)來管理整個(gè)系統(tǒng),可以使系統(tǒng)整體性能得到明顯優(yōu)化。μC/OS-II是一個(gè)完整、可固化、可裁剪、可移植的占先式實(shí)時(shí)多任務(wù)內(nèi)核,非常適合于在微處理器上進(jìn)行移植。ARM7系列微處理器是目前使用最為廣泛的微處理器之一,本文討論的μC/OS-II在ARM7上的移植有著較為典型的意
義。
二、μC/OS-Ⅱ移植概述
移植就是使一個(gè)內(nèi)核能夠在某個(gè)微處理器上或者控制器上運(yùn)行,也就是要為特定的CPU編寫特定的底層代碼。操作系統(tǒng)的移植涉及到處理器體系結(jié)構(gòu)和編譯器以及操作系統(tǒng)本身,是一項(xiàng)比較復(fù)雜的工作。μc/OS-Ⅱ的大部分代碼是用C語言編寫的,但移植時(shí)候還是需要用匯編語言編寫一些與CPU硬件相關(guān)的代碼。移植μC/OS-Ⅱ到一個(gè)微處理器上一般需要編寫三個(gè)文件:C語言頭文件OS_CPU.H,其中定義一些與編譯器無關(guān)的數(shù)據(jù)類型以及與處理器有關(guān)的常量和宏;C程序源文件OS_CPU_C.C,其中定義了μC/OS-Ⅱ任務(wù)堆棧初始化函數(shù)以及鉤子類函數(shù);匯編程序源文件OS_CPU_A.S,其中定義了中斷服務(wù)函數(shù)以及任務(wù)切換函數(shù)。這些移植文件的編寫在很多文章中都有過詳細(xì)的介紹[3][4],本文不再贅述。本文將從非運(yùn)行態(tài)任務(wù)的堆棧一致性這個(gè)角度來聯(lián)通各個(gè)主要移植函數(shù)的編寫,以加深對(duì)μC/OS- II移植的理解。
三、移植難點(diǎn)
操作系統(tǒng)能夠穩(wěn)定的運(yùn)行,關(guān)鍵在于任務(wù)能夠穩(wěn)定的切換。在μC/OS-Ⅱ中,任務(wù)通過模仿中斷的方式來運(yùn)行,并且擁有自己單獨(dú)的任務(wù)堆棧;處于非運(yùn)行態(tài)任務(wù)的堆棧結(jié)構(gòu)看起來就像剛發(fā)生過中斷一樣,并且所有的寄存器都已經(jīng)保存到堆棧中[2]。非運(yùn)行態(tài)任務(wù)堆棧的一致性指任務(wù)沒有處在運(yùn)行態(tài)時(shí)其任務(wù)堆棧中保存的任務(wù)環(huán)境順序具有一致性,這是任務(wù)切換能夠穩(wěn)定進(jìn)行的最主要因素。大部分的處理器會(huì)通過提供軟中斷或者是陷阱的方法來實(shí)現(xiàn)任務(wù)的切換。而涉及到任務(wù)切換或者說是任務(wù)堆棧變化的移植函數(shù)則是移植的難重點(diǎn),這些移植函數(shù)都要參考非運(yùn)行態(tài)任務(wù)的堆棧結(jié)構(gòu)來編寫,以保證任務(wù)堆棧結(jié)構(gòu)的一致性。本文將以μC/OS-Ⅱ在ARM7系列微處理器上的移植代碼來分析討論μC/OS-Ⅱ的移植難點(diǎn),編譯器為ADS1.2。注:移植程序來自周立功公司產(chǎn)品easyarm2200m[1]。
四、移植函數(shù)分析
涉及到任務(wù)堆棧的函數(shù)主要有堆棧初始化函數(shù)OSTaskStkInit(),任務(wù)級(jí)代碼中的任務(wù)切換函數(shù)OS_TASK_SW(),中斷退出時(shí)候的任務(wù)切換函數(shù)OSIntCtxSw(),首次進(jìn)入多任務(wù)環(huán)境的OSStartHighRdy()以及中斷匯編宏。
(一)函數(shù)OSTaskStkInit()
堆棧初始化函數(shù)OSTaskStkInit()由創(chuàng)建任務(wù)的函數(shù)調(diào)用,初始化非運(yùn)行態(tài)任務(wù)的堆棧結(jié)構(gòu),它與cpu的體系結(jié)構(gòu)有著密切的關(guān)系。這個(gè)堆棧結(jié)構(gòu)一旦確立,以后所有的任務(wù)環(huán)境的壓棧和出棧操作都必須參照這個(gè)堆棧結(jié)構(gòu)來處理堆棧。在ARM7系列處理器上的任務(wù)堆棧結(jié)構(gòu)如圖1所示:
圖1中的任務(wù)其它入棧數(shù)據(jù)是任務(wù)運(yùn)行之后才有的,堆棧剛初始化后的任務(wù)堆棧棧底指向PC。堆棧初始化的一般順序是先模擬中斷到來時(shí)候處理器的動(dòng)作,這里是保存中斷返回地址PC以及連接寄存器LR;然后保存剩余的CPU寄存器,但是SP沒有被保存在堆棧環(huán)境中,SP保存在任務(wù)控制結(jié)構(gòu)TCB中以用于任務(wù)的切換。SPSR是沒有必要保存在堆棧中的,因?yàn)槿魏沃袛嗷蛘弋惓4驍嗟娜蝿?wù)的狀態(tài)都保存在堆棧的CPSR中。OsEnterSum是保存開關(guān)中斷次數(shù)的變量,它的入棧使得各個(gè)任務(wù)開關(guān)中斷的狀態(tài)互不影響。
(二)函數(shù)OS_TASK_SW()
任務(wù)級(jí)代碼中的任務(wù)切換函數(shù)OS_TASK_SW()是通過軟件中斷0號(hào)功能來實(shí)現(xiàn)的,軟件中斷向量直接指向匯編語言程序段OSIntCtxSw(程序段B+C)。在討論切換函數(shù)之前先確定軟件中斷時(shí)候的堆棧結(jié)構(gòu)(參考程序段A),軟件中斷將使系統(tǒng)進(jìn)入管理模式。
SoftwareInterrupt
LDR SP, StackSvc ;設(shè)置管理模式下的堆棧指針
STMFD SP!, {R0-R3, R12, LR} ;保存任務(wù)環(huán)境,LR是管理模式下的
MOV R1,SP
MOV R3,SPSR
………………………
程序段A
調(diào)用OSIntCtxSw之前管理模式的堆棧結(jié)構(gòu)如圖2所示,位于系統(tǒng)模式下的任務(wù)堆棧并沒有開始保存任務(wù)環(huán)境。OSIntCtxSw函數(shù)首先做的就是保存任務(wù)環(huán)境于任務(wù)堆棧(注意:R3保存有管理模式下的SPSR即系統(tǒng)模式下的CPSR,管理模式下的LR保存有系統(tǒng)模式下的PC)。
OSIntCtxSw
;先保存當(dāng)前任務(wù)的任務(wù)環(huán)境
LDR R2,[SP,#20];獲取管理模式下的LR
LDR R12,[SP,#16] ;獲取R12
MRS R0,CPSR ;保存管理模式下的CPSR
MSR CPSR_c,#(NoInt | SYS32Mode);進(jìn)入不帶中斷的系統(tǒng)模式
MOV R1,LR
STMFD SP!, {R1-R2} ;保存PC,LR于任務(wù)堆棧
STMFD SP!,{R4-R12} ;保存R4-R12于任務(wù)堆棧
MSR CPSR_c,R0 ;恢復(fù)原先模式
LDMFD SP!,{R4-R7};獲取R0-R3,任務(wù)環(huán)境下的R4-R7已經(jīng)保存
ADD SP,SP,#8 ;出棧R12,PC,圖2堆棧指針指向棧底
MSR CPSR_c,#(NoInt | SYS32Mode)
STMFD SP!,{R4-R7};保存R0-R3于任務(wù)堆棧
LDR R1,=OsEnterSum
LDR R2,[R1] ;獲取OsEnterSum值
STMFD SP!,{R2, R3};保存任務(wù)環(huán)境的CPSR以及OsEnterSum
;保存當(dāng)前任務(wù)堆棧指針到當(dāng)前任務(wù)的TCB
LDR R1,=OSTCBCur;R1保存有OSTCBCur的地址值
LDR R1,[R1] ;R1保存有OSTCBCur的首元素地址值
STR SP,[R1] ;把SP的值賦給OSTCBCur的首元素
…………………… ; B1
程序段B
系統(tǒng)模式下面的SP指向的是任務(wù)堆棧,而管理模式下面的SP指向的是管理模式堆棧,它對(duì)于每個(gè)任務(wù)來說不是獨(dú)立的。程序段B在管理模式和系統(tǒng)模式下的來回跳轉(zhuǎn),為的只是在任務(wù)堆棧中保存如圖1所示的任務(wù)堆棧結(jié)構(gòu),并且將軟件中斷對(duì)管理模式下堆棧的影響消除即其堆棧指針恢復(fù)到棧底。程序語句B1會(huì)把OSTCBHighRdy賦值給OSTCBCur,把OSPrioHighRdy賦值給OSPrioCur,標(biāo)志新老任務(wù)交替的開始。
OSIntCtxSw_1;恢復(fù)新任務(wù)的任務(wù)環(huán)境
LDR R4,[R6];獲取新任務(wù)堆棧指針SP
ADD SP,R4,#68 ;此時(shí)SP指向圖1堆棧中PC+4
LDR LR,[SP,#-8] ;恢復(fù)LRC1
MSR CPSR_c,#(NoInt | SVC32Mode)
MOV SP,R4;設(shè)置管理模式下的堆棧指針
LDMFD SP!, {R4, R5};拷貝CPSR,OsEnterSum
LDR R3,=OsEnterSum
STR R4,[R3];恢復(fù)新任務(wù)的OsEnterSum
MSR SPSR_cxsf,R5 ;對(duì)管理模式下的SPSR進(jìn)行修改,以便恢復(fù)任務(wù)環(huán)境CPSR
LDMFD SP!,{R0-R12,LR,PC }^ ;恢復(fù)其它環(huán)境,運(yùn)行新任務(wù)
程序段C
程序語句C1恢復(fù)的是系統(tǒng)模式下的LR,在管理模式下是無法恢復(fù)的,因?yàn)楣芾砟J胶拖到y(tǒng)模式下LR寄存器的地址是不一樣的。程序用管理模式下SP指向任務(wù)堆棧棧頂,然后先恢復(fù)CPSR,OsEnterSum,最后恢復(fù)系統(tǒng)模式下的R0-R12,PC。注意程序段C最后一句恢復(fù)的LR是管理模式下的LR,它不屬于任務(wù)環(huán)境。寄存器組中含有PC并且有“^”后綴使得系統(tǒng)由管理模式返回系統(tǒng)模式,并且用管理模式的SPSR恢復(fù)新任務(wù)的CPSR,這樣返回的任務(wù)才能正確的切換CPU的模式和狀態(tài)。不難發(fā)現(xiàn)雖然程序比較復(fù)雜,但是依然是圍繞圖1的堆棧結(jié)構(gòu)來對(duì)舊任務(wù)進(jìn)行保存,對(duì)新任務(wù)環(huán)境進(jìn)行恢復(fù)。
(三)函數(shù)OSStartHighRdy()
OSStartHighRdy()函數(shù)為第一次調(diào)用運(yùn)行最高優(yōu)先級(jí)的任務(wù)。其編程比較簡單。其思想為把OSTCBHighRdy賦值給R6,然后直接轉(zhuǎn)到OSIntCtxSw_
1處執(zhí)行。
(四)函數(shù)OSIntCtxSw()以及中斷匯編宏
中斷退出時(shí)的任務(wù)切換函數(shù)OSIntCtxSw()只由OSIntExit()調(diào)用,而OSIntExit()只在中斷匯編宏中被調(diào)用。OSIntExit函數(shù)中重新設(shè)置了OSTCBHighRdy但是肯定不會(huì)進(jìn)行任務(wù)切換,因?yàn)橐浦渤绦蛑卸x了OSIntCtxSw()為return,任務(wù)切換功能由中斷匯編宏做出判斷后調(diào)用OSIntCtxSw程序段來實(shí)現(xiàn)。中斷匯編宏調(diào)用OSIntCtxSw之前要在中斷模式下?lián)碛袌D2相同的堆棧結(jié)構(gòu),并且R3要保存著中斷模式下的SPSR(系統(tǒng)模式下的CPSR)。這樣才能保證任務(wù)堆棧結(jié)構(gòu)在進(jìn)行切換的時(shí)候不至于出現(xiàn)混亂的現(xiàn)象。這些都是由IRQ匯編宏來實(shí)現(xiàn)。分析如下:
MACRO
………………………
SUB LR, LR, #4 ; 調(diào)整中斷返回地址
STMFD SP!, {R0-R3, R12, LR}; 保存任務(wù)環(huán)境同圖2D1
MRS R3, SPSR; 保存SPSR到R3
STMFD SP, {R3, SP, LR}^ ; 保存系統(tǒng)模式下的R3,SP,LR D2
……………………… ; OSIntNesting++
SUB SP, SP, #4*3 ;調(diào)整SP
MSR CPSR_c, #(NoInt | SYS32Mode)
CMP R1, #1
LDREQ SP, =StackUsr ;D3
BL $IRQ_Exception_Function ;調(diào)用C語言的中斷處理程序
MSR CPSR_c, #(NoInt | SYS32Mode)
……………………
BL OSIntExit
……………………
MSR CPSR_c, #(NoInt | IRQ32Mode); 切換回irq 模式
LDMFD SP, {R3, SP, LR}^ ; D4
LDR R0, =OSTCBHighRdy
LDR R0, [R0]
LDR R1, =OSTCBCur
LDR R1, [R1]
CMP R0, R1; D5
ADD SP, SP, #4*3
MSR SPSR_cxsf, R3
LDMEQFD SP!, {R0-R3, R12, PC}^
LDR PC, =OSIntCtxSw ; 進(jìn)行任務(wù)切換
MEND
程序段D
進(jìn)入中斷的時(shí)候,PC的值將會(huì)自動(dòng)復(fù)制到中斷模式下的LR中,CPSR復(fù)制到中斷模式下的SPSR中。這些都是和軟件中斷沒有什么太大的區(qū)別,但進(jìn)入的異常模式不同,一個(gè)是管理模式一個(gè)是中斷模式(OSIntCtxSw程序段的注釋是在管理模式下調(diào)用時(shí)的注釋,中斷模式調(diào)用時(shí)的注釋只要把程序段B注釋中的管理模式改為中斷模式即可)。在程序語句D1中保存的堆棧結(jié)構(gòu)和圖2一樣,是為了使得調(diào)用OSIntCtxSw程序段之后舊任務(wù)保存的堆??臻g和圖1一致。語句D2在中斷模式下對(duì)系統(tǒng)環(huán)境的保存是為了在調(diào)用中斷服務(wù)程序C語言函數(shù)時(shí)不破壞任務(wù)環(huán)境(特別是任務(wù)堆棧指針),保存的環(huán)境由編譯器和處理器共同決定,在調(diào)用C語言函數(shù)之后由語句D4進(jìn)行恢復(fù)。語句D3中的StackUsr是為中斷服務(wù)程序C語言部分準(zhǔn)備的堆棧棧頂,對(duì)任務(wù)堆棧沒有影響。語句D5比較OSTCBHighRdy和OCSTCBCur的任務(wù)堆棧指針,如果相等說明當(dāng)前任務(wù)為最高優(yōu)先級(jí)任務(wù),不用進(jìn)行任務(wù)切換;反之,則需要調(diào)用OSIntCtxSw程序段進(jìn)行任務(wù)切換。圖2的堆棧結(jié)構(gòu)其實(shí)是由中斷程序與軟件中斷程序共同決定的,一旦決定了以后兩者都必須嚴(yán)格遵從執(zhí)行,這樣兩者都調(diào)用OSIntCtxSw程序段的時(shí)候就能保證堆棧的一致性了。中斷匯編宏的編寫使得所有中斷服務(wù)程序的編寫都變得簡單,中斷只需要用C語言編寫其特定的中斷功能部分。
五、總結(jié)
本文較新穎地從堆棧結(jié)構(gòu)方面來分析了操作系統(tǒng)的移植。在上例的分析中,充分體現(xiàn)了堆棧結(jié)構(gòu)對(duì)任務(wù)切換以及代碼移植的重要性。從非運(yùn)行態(tài)任務(wù)堆棧結(jié)構(gòu)的一致性來聯(lián)通分析各個(gè)移植函數(shù)的編寫,讓移植過程變得更清晰明了。
參考文獻(xiàn):
[1]周立功,ARM嵌入式系統(tǒng)基礎(chǔ)教程[M].北京:北京航空航天大學(xué),2005.1.
[2]陳是知,Μc/OS-Ⅱ內(nèi)核分析、移植與驅(qū)動(dòng)程序開發(fā)[M].北京:人民郵電出版社,2007.9.
[3]宋暉、高小明,基于ARM的嵌入式操作系統(tǒng)Μc/OS-II的移植[J]. 微計(jì)算機(jī)信息,2006, 22(2):135-136.
[4]王明奇,μC/OS-Ⅱ在ARM7微處理器上的移植[J].電腦與信息技術(shù),2006,14(1):22-24.
作者簡介:
黃睿(1980-),男,佈依族,貴州省興仁縣人,研究生,副科,研究方向:計(jì)算機(jī)科學(xué)與技術(shù)、數(shù)據(jù)庫應(yīng)用。