當前位置:編程學習大全網 - 腳本源碼 - 如何學習 nucleus os

如何學習 nucleus os

內容:

壹、nucleus plus特點:

1.內核采用微內核的設計,方便移植,資料寫著更reliable,但是我不這麽認為,與linux相比,以ARM平臺為例,NU只用到了SVC mode,內核與用戶任務都運行在同壹個狀態下,也就是說所有的task都擁有訪問任何資源的權限,這樣很reliable麽?

2.real-time OS,NU是壹個軟實時操作系統(VxWorks是硬實時),thread control component支持多任務以及任務的搶占,對於中斷的處理定義了兩種服務方式,LISR和HISR,這個與linux中的上、下半部機制類似,linux中的下半部是通過軟中斷來實現的,NU的HISR只是作為壹種優先級總是高於task的任務出現。

3.NU是以library的方式應用的,通過寫自己的app task與裁剪後的NU內核及組件鏈接起來,NU並沒有CLI

二、組件

1.IN component

初始化組件由三個部分組成,硬件在reset後首先進入INT_initialize(),進行板級的相關初始化,首先設置SVC mode,關中斷,然後將內核從rom中拷貝至ram中,建立bss段,依次建立sys stack, irq stack和fiq stack,最後初始化timer,建立timer HISR的棧空間,看了壹下2410平臺的代碼,壹個tick大概是15.8ms,完成板級的初始化後就進入了INC_initialize,初始化各個組件,其中包括Application initialize,create task和HISR,最後將控制權交給schedule,主要看了壹下RAM中地址空間的安排

|timer HISR stack = 1024|

|FIQ stack = 512|

|IRQ stack = 1024|

|SVC stack = 1024|

|.bss|

|.data|

|.text|

其中SVC stack的大小與中斷源的個數相關,nested irq發生時,irq_context保存在SVC stack中,IRQ的stack只是做了臨時棧的作用。

2.thread control component

TC組件是NU內核的最重要組成部分,主要涵蓋了調度、中斷、任務的相關操作、鎖、時鐘幾個方面,下面分別介紹。

調度(schedule)

NU中的線程類型(在同壹個地址空間內)有兩種,HISR和task,HISR可以理解為壹種優先級較高的task,但又不是task,HISR優先級高於task的實現方式就是schdule時,先去查看當前是否有active的HISR,再去查看task。task有suspend、ready、finished和terminated四種狀態,而HISR只有executing和no-active這兩種狀態。

每壹個task都有壹個線程控制的數據結構(TCB thread control block),其中包括了task的優先級、狀態、時間片、task棧、protect信息、signal操作的標誌位和signal_handler等,task在創建時初始化這些信息,將task掛到壹個create_list上,初始設定task為pure_suspend,如果設定auto start,調用resume_task()喚醒task,這裏有個細節,如果在application initialize中create_task(),則task不會自動運行,因為初始化還未完成,控制權還沒有交給schedule,無法調度task。task被喚醒後狀態改變為ready,並掛在壹個TCD_Priority_List[256]上,數組的每個元素是壹個指向TCB環形雙向鏈表的指針,根據task的tc_priority找到對應優先級的TCB head pointer。

每壹個HISR都有壹個HISR控制的數據結構(HCB HISR control block),其中只有優先級,HISR棧和HISR entry信息,因此HISR是不可以suspend,同時也沒有time slice以及signal的相關操作,壹般情況下當發生了中斷後,HISR被activate,schedule就會調度HISR運行,期間如果不發生中斷,HISR的執行是不會被打斷的,HISR的優先級只有0、1、2,timer的HISR優先級為2,也就是說由外部中斷激活的HISR很難被搶占的,只有更高優先級的中斷HISR才可以。與task不同,被激活的HISR使用head_list和tail_list將HCB掛在壹個單項的鏈表上,因為相同優先級的HISR不會搶占對方,因此不需要雙向鏈表,使用兩個指針目的是加快HISR執行的速度。

壹個實時操作系統的核心就是對於任務的調度,NU的調度策略是time slice和round robin的算法,

調度的部分主要有三個函數control_to_system()用於保存上下文,建立solicited stack,關中斷,關system time slice,並重置task的time slice為預設值,將sp更新為system_stack_pointer,調用schedule(),調度的過程是非常簡單的查詢,就是查看兩個全局的變量,TCD_Execute_HISR和TCD_Execute_Task,schedule部分的關鍵是打開了中斷,不然如果當前沒有ready的task或是被激活的HISR,則shedule死循環下去,查詢到下壹個應該執行的線程後跳轉至control_to_thread(),在這裏重新開啟system time slice,然後將線程的tc_stack_ptr加入到sp中,切換至線程的棧中,依次pop出來,即完成了任務調度。

任務的切換主要是上下文的切換,也就是task棧的切換,函數的調用會保存部分regs和返回地址,這些動作都是編譯器來完成的,而OS中的任務切換是運行時(runtime)的壹種狀態變化,因此編譯器也無能為力,所以對於上下文的保存需要代碼來實現。

任務的搶占是異步的因此必須要通過中斷來實現,壹般每次timer的中斷決定當前的task的slice time是否expired,然後設置TCT_Set_Execute_Task為相同優先級的其他task或更高優先級的task;高優先級的task搶占低優先級的task,壹般是外部中斷觸發,在HISR中resume_task()喚醒高優先級的task,然後schedule到高優先級的task中,因為timer的HISR是在系統初始化就已經註冊的,只是執行timeout和time slice超時後的操作,並沒有執行resume_task的動作。

NU中的stack有兩種solicited stack和interrupt stack,solicited stack是壹種minmum stack,而interrupt stack是對當前所有寄存器全部保存,TCB中的minimum stack size = 申請得到stack size - solicited stack(在arm mode下占44字節,thumb mode下占48字節),thumb標誌用來記錄上下文保存時的ARM的工作模式,c代碼編譯為thumb模式,這樣可以減小code size,提高代碼密度,assembly代碼編譯為arm模式提升代碼的效率,NU中內核的代碼不多,主要是assembly代碼。stack的類型與其中PC指向的shell無關,interrupt stack保存的是task或是HISR在執行的過程中被中斷時的現場,solicited stack建立的地方包括 control_to_system()、schedule_protect()和send_signals()發送給占有protect資源的task的情況,HISR_Shell()執行完後會建立solicited stack,再跳轉至schedule。

(Lower Address) Stack Top -> 1 (Interrupt stack type)

CPSR Saved CPSR

r0 Saved r0

r1 Saved r1

r2 Saved r2

r3 Saved r3

r4 Saved r4

r5 Saved r5

r6 Saved r6

r7 Saved r7

r8 Saved r8

r9 Saved r9

r10 Saved r10

r11 Saved r11

r12 Saved r12

sp Saved sp

lr Saved lr

(Higher Address) Stack Bottom-> pc Saved pc

(Lower Address) Stack Top -> 0 (Solicited stack type)

!!FOR THUMB ONLY!! 0/0x20 Saved state mask

r4 Saved r4

r5 Saved r5

r6 Saved r6

r7 Saved r7

r8 Saved r8

r9 Saved r9

r10 Saved r10

r11 Saved r11

r12 Saved r12

(Higher Address) Stack Bottom-> pc Saved pc

壹個簡單的例子說明stack的情況,首先是壹個task在ready(executing)的狀態下,而且time slice超時了,timer中斷發生後,保存task上下文interrupt_contex_save(),在task的tc_stack_ptr指向的地方建立中斷棧

taskA |interrupt stack|___tc_stack_ptr 棧頂端是pc=lr-4

ARM對於中斷的判定發生在當前指令完成execute時,同時pipeline的原因pc=pc+8,入棧時就把lr-4首先放在stack的最高端(high)。

timer的LISR完成後激活了HISR,執行TCC_Time_slice()將當前task移到相同優先級的尾端,並且設置下壹個要執行的task,HISR在棧頂端保存的是這個HISR_shell的入口地址,因為task的執行完就finished,HISR是可重入的

HISR |solicited stack| ?棧頂端是HISR_shell_entry

中斷(interrupt)

前面已經提及了中斷的基本操作,這裏就寫壹些代碼路徑的細節,中斷的執行主要是兩個部分LISR和HISR,分成兩個部分的目的就是將關中斷的時間最小化,並且在LISR中開中斷允許中斷的嵌套,以及建立中斷優先級,都可以減少中斷的延遲,保證OS的實時性。

NU的中斷模式是可重入的中斷處理方式,也就是基於中斷優先級和嵌套的模式,中斷的嵌套在處理的過程中應對lr_irq_mode寄存器進行保存,因為高優先級的中斷發生時會覆蓋掉低優先級中斷的r14和spsr,因此要利用系統的棧來保存中斷棧。

NU對於中斷上下文的保存具體操作如下:

(1)在中斷發生後執行的入口函數INT_IRQ()中,將r0-r4保存至irq的棧中

(2)查找到對應的interrupt_shell(),clear中斷源,更新全局的中斷計數器,然後進行interrupt_contex_save

(3)首先利用r1,r2,r3保存irq模式下的sp,lr,spsr,這裏sp是用來切換至系統棧後拷貝lr和spsr的,這裏保存lr和spsr是目的是task被搶占後,當再次schedule時可以返回task之前的狀態。

(4)切換至SVC模式,如果是非嵌套的中斷則保存上下文至task stack中,將irq模式下的lr作為頂端PC的返回值入棧,將SVC模式下的r6-r14入棧,將irq模式下的sp保存至r4中入棧,最後將保存在irq_stack中的r0-r4入棧

(5)如果是嵌套中斷,中斷的嵌套發生在LISR中,在執行LISR時已經切換至system stack,因此嵌套中斷要將中斷的上下文保存至system stack中,與task stack中interrupt stack相比只是少了棧頂用來標記嵌套的標誌(1 not nested)

(6)有壹個分支判斷,就是如果當前線程是空,即TCD_Current_Thread == NULL,表明當前是schedule中,因為初始化線程是關中斷的,這樣就不為schedule線程建立棧幀,因為schedule不需要保存上下文,在restore中斷上下文時直接跳轉至schedule。

中斷上下文的恢復

全局的中斷計數器INT_Count是否為0來判定當前出棧的信息,如果是嵌套則返回LISR中,否則切換至system stack執行schedule

timer

timer與中斷緊密相關,其實timer也是中斷的壹種,只是發生中斷的頻率較高,且作用重大,壹個實時操作系統,時間是非常重要的壹部分,NU中的timer主要有四個作用:

(1)維護系統時鐘 TMD_system_clock

(2)task的time slice

(3)task的suspend timeout timer

(4)application timer

其中(3)(4)***用壹種機制,壹個全局的時間軸TMD_timer,timeout timer和app timer都建立在壹個TM_TCB的數據結構上,通過tm_remaining_time來表征當前timer的剩余時間,例如當前有timer_list上有三個TM_TCB,依次是Ta = 5,Tb = 7, Tc = 20,那麽建立的鏈表上剩余時間依次是5,2,8,如果現在要加入壹個新的timer根據timer值插入至合適的位置,如果插入的timer為13,則安排在Tb後面,剩余時間為1,後面的8改為7,當發生了timer expired,則觸發timer_HISR,如果是app timer則執行timer callback,如果是task timeout timer,則執行TCC_Task_Timeout喚醒task。

(2)的實現也是依賴於全局的time slice時間軸,每壹個task在執行時都會將自己的時間片信息更新至全局的時間軸上,當壹個task的time slice執行完在timer HISR中調用TCC_task_Timeout將當前的task放在相同優先級list的最尾端,並設置下壹個最高優先級的任務。task在執行的過程中只有被中斷後time slice會保存下來,其他讓出處理器的情況都會將time slice更新為預設值。

protect

protect與linux的鎖機制類似,互斥訪問,利用開關中斷來實現,並且擁有protect的task是不可以suspend的,必須要將protect釋放後才可以掛起,當壹個優先級較低的task占有protect資源,如果被搶占,壹個高優先級的task或HISR在請求protect資源時會執行TCC_schedule_protect()讓出處理器給低優先級的task執行,直到低優先級的task執行unprotect()為止,此時task或HISR建立的是solicited stack,同時在control_to_thread前開關中斷壹次,這樣可以減少壹次上下文的切換。NU中常用到的是system_protect,它就是壹把大鎖,保護內核中所有全局數據結構的順序訪問,粒度很大。

LISR中不可以請求protect資源,因為LISR是中斷task後執行,如果task占有protect資源,這時LISR又去請求protect資源,會發生死鎖,因為LISR讓出處理器後,schedule沒辦法再次調度到LISR執行,則發生死鎖錯誤,因此在LISR中除了activate_HISR()以外不可以使用system call,例如resume_task等等,這寫系統調用都會請求protect資源。

對於protect的請求按照壹定的順序可以防止死鎖,NU的源碼中壹般將system_protect資源的請求放在後面,其他如DM_protect先請求。

  • 上一篇:蔡少芬年輕的時候這麽漂亮,妳看過她出演的多少電視劇
  • 下一篇:隱王漫畫版完結了沒有,壹***多少話?單行本有多少本啊?宵風有沒有復活啊最後
  • copyright 2024編程學習大全網