官术网_书友最值得收藏!

2.2 嵌入式操作系統的功能

嵌入式操作系統隔離了用戶與計算機系統的硬件,向用戶提供一個比裸機功能更強的虛擬計算機系統。各種嵌入式操作系統支持的功能不盡相同,一個具體的嵌入式操作系統會根據需要有選擇地支持以下的某些功能:任務管理、任務通信、內存管理、中斷管理、文件管理、輸入/輸出管理、時間管理、電源管理、看門狗。其中任務管理、任務通信、中斷管理是嵌入式操作系統的基本功能。

2.2.1 任務管理

任務是一個具有獨立功能的程序段的一次運行活動,是操作系統內核進行調度的基本單位。在不支持線程的操作系統中,它相當于進程。在支持線程的操作系統中,它相當于線程。

任務管理是嵌入式操作系統的一項基本功能。這種功能由任務建立、任務刪除、任務阻塞、任務喚醒、任務睡眠、任務屬性設置、任務屬性查詢、任務調度等多項具體的功能組成。這些功能可以引起任務在各種狀態之間的轉換。在各項功能中,任務調度是關鍵。任務管理功能一般由嵌入式操作系統的內核來實現。

在介紹各種任務管理功能之前,先介紹幾個與任務管理密切相關的概念。它們分別是任務狀態、任務控制塊和任務隊列。

1. 任務狀態

任務狀態代表了任務占有系統資源的狀況。不同嵌入式操作系統支持的任務狀態雖然略有不同,但都有就緒、運行、阻塞3種基本狀態。

① 就緒狀態:就緒狀態是任務已經具備被運行的條件,正在等待被運行的狀態。

② 運行狀態:運行狀態是任務已經獲得了CPU資源,正在被運行的狀態。

③ 阻塞狀態:阻塞狀態是任務正在等待某種事件發生的狀態。如等待某一資源已經可以使用的通知。

在一定的條件之下,任務的狀態會在這3種狀態之間發生轉換,這種轉換關系如圖2.10所示。發生轉換的原因如下:

① 任務被建立后將首先進入就緒狀態。

② 處于就緒狀態的任務,如果被操作系統內核中的調度程序選中,獲得了CPU的使用權后就將進入運行狀態,在CPU上運行。

③ 處于運行狀態的任務如果執行權被具有更高優先級的任務搶占或運行時間超時,就會重新回到就緒狀態。

④ 處于運行狀態的任務如果需要使用某種資源并且暫時得不到滿足時,就會進入到阻塞狀態。

⑤ 處于阻塞狀態的任務,如果需要使用的資源已經可以在系統中得到,就會轉換到就緒狀態。

圖2.10 任務狀態間的轉換關系

2. 任務控制塊

嵌入式操作系統對任務的管理通過任務控制塊(Task Control Block,TCB)來實現。任務控制塊是一個包含與任務相關的信息的數據結構。

如圖2.11所示,任務控制塊包含了任務執行過程中需要用到的各種信息。不同嵌入式操作系統的任務控制塊所包含的信息雖不太一樣,但一般都包括任務名字、任務標識號、任務執行起始地址、任務狀態、任務優先級、任務上下文等內容。

圖2.11 任務控制塊

任務上下文中保存了任務執行過程中某一時刻CPU寄存器中的內容。當進行任務切換時,退出運行的任務的寄存器值將被保存到任務控制塊的任務上下文中,投入運行的任務的上下文則被恢復到CPU寄存器中。

由于嵌入式系統的資源有限,嵌入式操作系統可以支持的最大任務數量通常要預先進行設定。嵌入式操作系統在初始化的過程中,將按照預先設定的最大任務數建立空閑的任務控制塊,并形成一個空閑任務控制塊隊列。在建立任務時,嵌入式操作系統從空閑任務控制塊隊列中為被建立的任務分配一個任務控制塊,并將與任務有關的已知信息保存到任務控制塊中。其后對任務進行的各種操作都是基于這個任務控制塊的。在任務被刪除之前,任務控制塊中的信息可以通過嵌入式操作系統提供的系統調用進行修改,或隨著系統運行過程中發生的事件而變化。當任務被刪除后,對應的任務控制塊會被回收到空閑任務控制塊隊列中。

3. 任務隊列

任務隊列是由任務控制塊所構成的隊列。嵌入式操作系統在進行任務管理時要用到多種不同的任務隊列,包括就緒隊列、阻塞隊列和空閑隊列。組織各種隊列的方式也有多種,單阻塞隊列方式和多阻塞隊列方式是兩種典型的隊列組織方式。

如圖2.12所示,單阻塞隊列方式比較簡單。這種方式把任務的任務控制塊組織為兩個隊列,一個是就緒隊列,另一個是阻塞隊列。如果任務擁有除CPU以外的其他所有資源,則其任務控制塊被放在就緒隊列中,等待投入運行。任務在運行過程中得不到需要的資源就會變為阻塞狀態,其任務控制塊就會被放到阻塞隊列中。同時操作系統會按照一定的策略在就緒隊列中選擇另外一個任務投入運行(處于運行狀態任務的任務控制塊也可作為一個特殊的成員放在就緒隊列中)。處在運行狀態的任務如果運行權被其他高優先級的任務搶占或運行時間超時,其任務控制塊就會從阻塞隊列移到就緒隊列中,等待再次運行。處于阻塞狀態的任務如果需要的資源得到了滿足,其任務控制塊也會從阻塞隊列移到就緒隊列中。

圖2.12 單阻塞隊列方式

采用單阻塞隊列方式時,如果一個資源得到釋放,操作系統內核需要掃描整個阻塞隊列,搜索需要該資源的任務,并按照一定的策略把這一資源分配給其中的某個任務,然后把該任務的任務控制塊放到就緒隊列中。在系統任務比較多時,如果采用單阻塞隊列方式,搜索需要資源的任務所花費的時間就比較長,會使系統的實時性能受到影響。

采用多阻塞隊列方式可以避免單阻塞隊列的不足,提高操作系統的實時性能。多阻塞隊列方式如圖2.13所示。這種隊列方式為每一種資源建立一個阻塞隊列。因此,在一個資源被釋放時,只需要在該資源的阻塞隊列中選擇任務,所以能夠在較短的時間內確定該資源應該被分配給哪個任務。

在采用基于優先級的調度策略時,無論采用單阻塞隊列方式還是多阻塞隊列方式,對于就緒隊列通常有以下兩種管理方法。

① 就緒隊列中的任務控制塊按照任務進入就緒狀態的時間排列。采用這種管理方法,調度程序需要從就緒隊列的頭部到尾部進行一次掃描才能找到系統中優先級最高的任務。但在一個任務變為就緒狀態時,只需簡單地將其任務控制塊放在就緒隊列的末尾。

② 就緒隊列中的任務控制塊按照任務的優先級排列。采用這種管理方法,調度程序能很快找到系統中優先級最高的任務,但在一個任務變為就緒狀態時,需要將其任務控制塊插到就緒隊列中的合適位置上,以確保就緒隊列中的任務控制塊仍然保持正確的次序關系。

圖2.13 多阻塞隊列方式

4. 任務建立

任務建立功能就是建立一個新任務,并返回一個標識該任務的標識號。隨后用戶可以通過這個標識號進行與任務相關的其他操作。在建立任務時一般需要提供以下的信息:

① 任務名字;

② 任務優先級;

③ 任務堆棧大小;

④ 任務屬性;

⑤ 任務執行起始地址;

⑥ 傳遞給任務的參數。

由于不同任務運行時需要的堆??臻g大小差別很大,很難由嵌入式操作系統來統一指定。因此,通常由用戶在建立任務時指定任務堆棧的大小。

任務屬性通常包括任務是否可被搶占、任務所擁有的時間片大小、任務是否可響應信號、任務是否使用數字協處理器等。

建立一個任務時,嵌入式操作系統通常需要完成以下工作:

① 從空閑任務控制塊隊列中為新建立的任務分配一個任務控制塊;

② 根據用戶提供的信息初始化任務控制塊;

③ 為任務分配一個可以唯一標識任務的標識號,并將這個標識號返回給用戶;

④ 使任務處于就緒狀態,并把任務放到就緒隊列中;

⑤ 進行任務調度。

5. 任務刪除

任務刪除功能根據指定的標識號刪除一個任務。一個任務在運行過程中會使用各種各樣的資源,在刪除一個任務時,需要釋放該任務所擁有的所有資源。釋放資源的工作通常由操作系統和任務本身共同完成。在刪除任務的系統調用時,操作系統只釋放那些由它為任務分配的資源,如任務控制塊所占用的空間。那些由任務自己分配的資源,則由任務自己釋放,如任務自己申請的內存空間、自己申請的信號量等。

刪除一個任務時,嵌入式操作系統通常需要完成以下工作:

① 根據指定的標識號獲得任務的任務控制塊;

② 把任務的任務控制塊從就緒隊列或者阻塞隊列中取出,加入到空閑任務控制塊隊列中;

③ 釋放操作系統為任務分配的資源。

6. 任務阻塞

任務阻塞功能根據指定的標識號阻塞一個任務。任務被阻塞后,將處于阻塞狀態。一個任務可以通過這一功能把自己阻塞。當任務把自己阻塞后,會引起操作系統進行任務調度,選取另外一個合適的任務投入運行。

一個任務阻塞時,操作系統通常需要完成以下工作:

① 根據指定的標識號獲得任務的任務控制塊;

② 把任務的狀態變為阻塞狀態,并把任務控制塊放到阻塞隊列中;

③ 如果是任務自己阻塞自己,則調用調度程序,進行任務調度。

7. 任務喚醒

任務喚醒功能根據一個任務是否還在等待其他資源,決定是否將該任務喚醒。如果任務還在等待其他資源,則任務仍將處于阻塞狀態,否則將任務變為就緒狀態。

喚醒一個任務時,操作系統通常需要完成以下工作:

① 根據指定的標識號獲得任務的任務控制塊;

② 判斷任務是否還在等待其他資源,如果還在等待其他資源,則任務仍將處于阻塞狀態;否則,把任務的狀態變為就緒狀態,并把其任務控制塊放到就緒隊列中;

③ 調用調度程序,進行任務調度。

8. 任務睡眠

任務睡眠功能使任務暫短地進入阻塞狀態,當設定的時間到達后,再使任務重新回到就緒狀態。

使一個任務睡眠時,操作系統通常需要完成以下工作:

① 根據指定的標識號獲得任務的任務控制塊;

② 把任務的狀態改變為阻塞狀態;

③ 設置睡眠時間(可將睡眠時間記錄在任務控制塊中);

④ 將任務的任務控制塊放到時間阻塞隊列中;

⑤ 調用調度程序,進行任務調度。

9. 任務信息設置

通過任務信息設置功能可以設置任務的優先級、是否可搶占、時間片、是否可響應信號、是否使用數字協處理器等屬性。

設置任務信息時,操作系統通常需要完成以下工作:

① 根據指定的標識號獲得任務的任務控制塊;

② 根據給定的值在任務控制塊中修改任務優先級和其他任務屬性的值;

③ 根據任務優先級和其他任務屬性的變化情況進行相應的處理。

10. 任務信息查詢

通過任務信息查詢功能可以查詢任務的優先級、是否可搶占、時間片、是否可響應信號、是否使用數字協處理器等屬性值。

查詢任務信息時,操作系統通常需要完成以下工作:

① 根據指定的標識號獲得任務的任務控制塊;

② 從任務控制塊中取出要查詢的內容;

③ 返回查詢結果。

11. 任務調度

任務調度是在多任務環境下產生的一個概念,其作用是通過一定的調度算法確定任務的執行順序和執行時間的長短。一種調度算法可認為是在一個特定時刻用來選擇將要運行的任務及其運行時間的一組規則。在發生以下情況后,操作系統通常就要進行任務調度。

① 中斷服務程序結束運行(當前運行任務的運行權被其他高優先級的任務搶占或運行時間超時);

② 當前運行的任務因需要某一資源而進入了阻塞狀態;

③ 某一任務進入了就緒狀態。

基于不同的準則,可以對操作系統的調度方法作不同的劃分。主要有:離線調度和在線調度、靜態優先級調度和動態優先級調度、搶占式調度和非搶占式調度。

(1)離線調度和在線調度

根據獲得調度信息的時機,可將調度方式分為離線(off-line)調度和在線(on-line)調度兩類。采用離線調度的前提是:進行調度所用的信息(如任務的運行截止時間、運行時間、運行過程中到達的時間等各種時間約束特性,任務的優先級等)在系統運行前就能夠完全確定。離線調度具有時間確定性,但缺乏靈活性,適用于那些程序的運行特性能夠預先確定,且不容易發生變化,同時有很強實時性要求的情況。在線調度所用的調度信息在系統運行過程中動態獲得。在線調度有較強的靈活性,適用范圍也比離線調度廣。

(2)靜態優先級調度和動態優先級調度

采用基于優先級的調度策略時,根據任務優先級的確定時機,可將調度方式分為靜態優先級調度和動態優先級調度兩類。采用靜態優先級調度時,任務的優先級在建立任務時確定,且在運行過程中不會發生變化。這種調度方式適用于能夠基本把握系統中所有任務的時間約束特性的情況。靜態優先級調度實現簡單,運行代價也比較低,但缺乏靈活性。采用動態優先級調度時,任務的優先級在運行中確定,并可能不斷發生變化。動態優先級調度靈活性強,但這種調度方式需要消耗更多的資源。

(3)搶占式調度和非搶占式調度

根據任務運行過程中,其運行權能否被其他任務搶占,可將調度方式分為搶占式調度和非搶占式調度兩類。搶占式調度通常是基于優先級的調度。采用搶占式調度時,正在運行的低優先級任務的運行權可以被其他高優先級任務搶占。只要是在臨界區代碼段之外,高優先級任務一旦準備就緒,就可以搶占低優先級任務的運行權。實時嵌入式操作系統一般采用搶占式調度,以使關鍵任務能夠打斷非關鍵任務的執行,確保關鍵任務的運行時間能夠得到保障。搶占式調度的優點是實時性好,反應快,但搶占式調度比較復雜,需要更多的資源,并且可能造成低優先級任務長時間得不到運行的情況。采用非搶占式調度方法時,一旦一個任務開始運行,該任務只有在運行完畢,而主動放棄CPU時,或是因為等待其他資源被阻塞時,才會停止運行。

搶占式調度和非搶占式調度之間的本質區別在于:一旦有高優先級任務進入就緒狀態,是否可以搶占當前任務的運行權,立刻被投入運行(只要在臨界區代碼段之外)。在系統運行過程中,使任務進入就緒狀態的情況有兩種:一是系統發生中斷,中斷服務程序使任務進入就緒狀態;二是當前運行的任務調用操作系統的系統調用使任務進入就緒狀態。下面對比一下在中斷服務程序使任務進入就緒狀態的情況下,搶占式調度和非搶占式調度兩種方式對系統運行過程所產生的影響。

1)采用非搶占式調度時,發生中斷后系統的運行過程

采用非搶占式調度時,如果中斷服務程序使一個高優先級任務變為就緒狀態,也必須等到當前運行的任務主動放棄對CPU的使用權后,新就緒的高優先級任務才能投入運行。因為無法確定低優先級的任務何時才能結束運行,所以采用非搶占式調度時,系統的響應時間是不確定的,實時性比較差。

如圖2.14所示是采用非搶占式調度時系統的運行過程示例。采用這種調度方法時,系統的執行過程如下:

① 在低優先級任務運行過程中發生中斷請求;

② CPU的使用權交給中斷服務程序(可能經過一定時間的中斷延遲);

③ 中斷服務程序使高優先級任務變為就緒狀態;

④ 中斷服務程序運行完畢,CPU的使用權交還給被中斷的低優先級任務;

⑤ 低優先級任務繼續運行;

⑥ 低優先級任務釋放CPU(原因可以是運行完畢、因為需要某種資源被阻塞或運行時間超時之一),操作系統進行任務調度,高優先級任務獲得CPU的使用權;

⑦ 高優先級任務運行。

圖2.14 采用非搶占式調度時系統的運行過程

2)采用搶占式調度時,發生中斷后系統的運行過程

采用搶占式調度時,如果中斷服務程序使一個優先級更高的任務進入就緒狀態,那么在中斷處理結束后,高優先級的任務便開始運行。由于中斷服務程序的運行時間可以大致估算,所以采用搶占式調度時,系統的響應時間相對比較確定。

如圖2.15所示是采用搶占式調度時系統的運行過程示例。采用這種調度方法時,系統的執行過程如下:

① 在低優先級任務運行過程中發生中斷請求;

② CPU的使用權交給中斷服務程序(可能經過一定時間的中斷延遲);

③ 中斷服務程序使高優先級任務變為就緒狀態;

④ 中斷服務程序運行完畢后,操作系統進行任務調度,使高優先級任務獲得CPU的使用權;

⑤ 高優先級任務運行;

⑥ 高優先級任務釋放CPU(原因可以是運行完畢、因為需要某種資源被阻塞或運行時間超時之一),操作系統進行任務調度,低優先級任務獲得CPU使用權;

⑦ 低優先級任務從被中斷的地方繼續運行。

圖2.15 采用搶占式調度時系統的運行過程

先來先服務(First Come First Serve,FCFS)、輪轉(Round Robin,RR)、最短作業優先(Shor-test Job First,SJF)是幾種在通用操作系統中采用較多的調度算法。但它們不太適用于實時嵌入式操作系統。為適應實時應用的需要,實時嵌入式操作系統在進行任務調度時通常采用搶占式最高優先級優先(Highest Priority First,HPF)算法。在采用此種算法時,優先級可以靜態確定,也可以動態確定。適用于實時嵌入式操作系統的動態優先級算法有單調速率(Rate-Monotonic Scheduling,RMS)算法、最早截止期優先(Earliest Deadline First,EDF)算法、最短空閑時間優先(Least Laxity First,LLF)算法等幾種。

(1)搶占式最高優先級優先算法

采用搶占式最高優先級優先調度算法時,每個任務被賦予一個優先級。這個優先級體現了任務對實時性的要求。任務的實時性要求越高,其優先級就越高。在系統運行的過程中,如果有優先級更高的任務進入就緒狀態,則當前任務立即停止運行,把CPU的使用權交給這個優先級更高的任務,使它立刻投入運行。這樣保證了CPU總是在運行優先級最高的任務。

一些非實時的操作系統雖然也可能會采用基于優先級調度算法,但一般不會是搶占式的,而且任務的優先級一般相差不大,為了體現系統的公正性,隨著任務的運行,往往還要根據耗用的時間,逐步降低其優先級。這樣,一個優先級很高的任務也不能長時間占住CPU不放,保證了系統的公正性。但是采用這種調度方法,任務的實時性要求是得不到保障的,因為哪怕在一開始賦予了一個任務很高的優先級,隨著時間的推移,其優先級也會慢慢降低,達到低于其他任務的水平。

在實時嵌入式操作系統中做法則大不一樣。它或者是采用靜態的優先級,使實時性強的任務總是有很高的優先級;或者在動態調整優先級時總是照顧實時性強的任務(采用單調速率、最早截止期優先、最短空閑時間優先等算法都會產生這樣的結果)。這樣做雖然破壞了系統的公正性,使某些優先級低的任務長時間不能被運行,但滿足了高實時性的要求,使它們只要一進入就緒狀態,馬上就能投入運行。這樣,系統中就出現了兩類不同性質的任務,一類是實時任務,另一類是普通任務。只要有實時任務在等待運行,普通任務就沒有運行的機會。

(2)單調速率算法

單調速率(Rate-Monotonic Scheduling,RMS)算法確定任務優先級的依據是任務的執行頻率。它將最高的優先級賦予執行頻率最高的任務,并以單調下降的順序對其他的任務分配優先級。

(3)最早截止期優先算法

最早截止期優先(Earliest Deadline First,EDF)算法分配給每個任務的優先級根據它們對運行截止時間的要求而定。運行截止時間最近的任務具有最高優先級,而運行截止時間最久的任務有最低優先級。

(4)最短空閑時間優先算法

最少空閑時間優先(Least Laxity First,LLF)算法根據任務的空閑時間確定任務的優先級??臻e時間越短,優先級越高。空閑時間等于運行截止時間減去任務的剩余運行時間。

理論上,最早截止期優先算法和最短空閑時間優先算法都是單處理器下確定任務優先級的最優算法。但是由于它們在每個調度時刻都要計算任務的運行截止時間或空閑時間,并根據計算結果改變任務優先級,因此開銷很大,不易實現,應用受到了一定的限制。在實際應用中,這些算法一般是和離線調度方式相結合。采用離線調度方式時,計算任務運行截止時間或空閑時間的要素在系統運行前確定(可以通過多次模擬運行并加以統計的方法得到),在系統運行過程中不會產生很大的開銷。

引入優先級的概念,并采用搶占式最高優先級優先調度算法雖然提高了系統的實時性能,但也帶來了其他一些問題。如果對這些問題處理不當,不但系統的實時性不能提高,反而會降低。其中的一個重要問題就是優先級反轉。

優先級反轉是一種因高優先級任務需要使用被低優先級任務占用的資源,形成高優先級任務等待低優先級任務的反常情況。如果在高優先級的任務被迫等待低優先級任務時,低優先級任務的運行權又被其他任務搶占,優先級反轉的情況將進一步惡化,致使高優先級任務長時間不能得到運行。

下面通過一個實例來說明采用搶占式最高優先級優先調度算法時為什么會出現優先級反轉。在這個實例中有3個并發運行的任務。

①任務A:在3個任務中首先進入就緒狀態,優先級最低,運行過程中需要使用共享資源S。

②任務B:在3個任務中第二個進入就緒狀態,優先級最高,運行過程中需要使用共享資源S。

③任務C:在3個任務中最后進入就緒狀態,優先級居中,運行過程中不需要使用共享資源S。

這3個任務運行的過程如圖2.16所示。任務A首先進入就緒狀態,經過任務調度后開始運行。在運行過程中使用了共享資源S,并通過互斥信號量禁止了其他任務使用該資源。這時任務B就緒。由于任務B的優先級高于任務A,因而搶占了任務A的運行權,開始運行。任務B在執行過程中也要使用共享資源S,但由于任務A還沒有使用完資源S,將其釋放,因此任務B得不到資源S的使用權,不得不阻塞自己,等待任務A釋放資源S,所以任務A得以繼續運行。此時,已經出現了優先級反轉。接著任務C就緒,由于其優先級高于任務A,因而搶占了任務A的運行權,這樣,情況進一步惡化,致使優先級最高的任務B不能最先運行完成(從圖2.16中可以看到優先級居于中間的任務C將首先運行完成)。

圖2.16 發生優先級反轉的過程

在實時嵌入式操作系統中,解決優先級反轉問題的方法主要有兩個,一個是優先級繼承,另一個是優先級封頂。我們通過上面的例子來解釋優先級繼承和優先級封頂的概念,并對兩者的特點做一個比較。

優先級繼承是指,當出現一個任務需要使用某一資源,并且該資源已經被其他一個低優先級任務所占用時,操作系統就將低優先級任務的優先級提高到與該任務相同的水平(優先級繼承)。在低優先任務使用完這一資源后再將其優先級設置回原有的水平。這樣可以使占有資源的低優先級任務盡快地釋放出阻塞任運行的資源,使優先級反轉所造成的危害限制在很小的范圍內,避免了情況的進一步惡化。

圖2.17 采用優先級繼承方法時的運行過程

采用優先級繼承方法時,上面例子中的3個任務的運行過程如圖2.17所示。首先是任務A開始運行。在運行過程中它使用了共享資源S,并通過互斥信號量禁止了其他任務使用該資源。然后任務B就緒,并搶占了任務A的運行權開始運行。當任務B需要使用共享資源S時,該資源正在被任務A占用,任務B得不到該資源。這時,操作系統通過比較任務A與任務B的優先級之后發現任務A的優先級小于任務B的優先級,因此就會將任務A的優先級提高到與任務B相同的水平,即發生了優先級繼承。任務C進入就緒狀態后,由于任務A的優先級已經被提高,所以任務C不能搶占它的運行權。到任務A釋放資源S后,它的優先級又被恢復到原有的水平,因此它的運行權被任務B搶占。此后,任務B、任務C、任務A依次運行完畢??梢姴捎脙炏燃壚^承方法能夠保證優先級最高的任務最先運行完。

優先級封頂是指,當一個任務需要使用某個資源時,立刻把該任務的優先級提高到需要使用該資源的所有任務中的最高優先級(這個優先級稱為該資源的優先級頂),并在該任務釋放這一資源后再將其優先級設置回原有的水平。

采用優先級封頂方法時,上面例子中的3個任務的運行過程如圖2.18所示。首先是任務A開始運行,當它使用共享資源S時,其優先級將被提高到資源S的優先級頂。注意,這個優先級頂可能比任務A、B、C的優先級都要高。由于此時任務A的優先級比任務B高,所以在任務B就緒之后,它仍然能夠順利地運行下去,直至釋放出共享資源S。任務A釋放出共享資源S后,其優先級被設置回原有值,因此運行權被任務B搶占。注意,當任務B使用共享資源S時,它的優先級也要被提高。此后,任務B、任務C、任務A依次運行完畢??梢姴捎脙炏燃壏忭敺椒ㄒ脖WC了優先級最高的任務能最先運行完。

圖2.18 采用優先級封頂方法時的運行過程

優先級繼承和優先級封頂兩種方法都是通過改變任務優先級的方法來解決優先級反轉問題,但它們改變優先級的時間和改變的范圍有所不同。優先級繼承只在占有資源的低優先級任務阻礙了高優先級任務運行時,才更改低優先級任務的優先級。所以這種方法比較精細,不會對任務的優先級做無用的改變,對任務的運行流程影響較小,但通常會發生較多次的任務切換。優先級封頂方法則不管一個任務是否阻礙了高優先級任務的運行,只要任務使用一個共享資源,其優先級都會被提升到需要使用該共享資源任務的最高優先級。所以這種方法對任務優先級所做的改變有可能是不必要的,對任務的運行過程的影響較大,但通常會使任務切換的次數有所減少。

2.2.2 任務通信

在并發環境下,若一個任務不受其他任務的影響,則稱該任務為獨立任務;若一個任務會受到其他任務的影響,則稱該任務和影響它的任務為協作任務。協作任務之間的關系有互斥、同步、數據交換3種。這3種關系統稱為任務通信。

① 互斥:指多個任務不能同時訪問同一資源。如CPU、打印機、數據等。這些不能被同時訪問的資源稱為臨界資源。訪問臨界資源的代碼段稱為臨界區。

② 同步:指一個任務能否繼續執行需要受到另一個任務的制約。如打印任務必須等計算任務完成計算工作之后才能打印計算結果。

③ 數據交換:目的是為了在任務之間傳遞一批數據。任務之間在進行同步時雖然也要相互交換數據,但其數據量很小,只是為了傳遞一個通知信息,而數據交換所傳遞的數據量一般比較大。

任務通信是嵌入式操作系統提供的一項基本功能。嵌入式操作系統提供的任務通信機制通常有信號量、事件、信號、消息郵箱、消息隊列、共享內存、管道等若干種。一個具體的嵌入式操作系統會有選擇地支持其中的若干種。這些任務通信機制一般由嵌入式操作系統的內核來實現。

1. 信號量

信號量也稱信號燈。它是一種最常用的任務通信機制。這種通信機制借鑒了交通管制中的信號燈原理。需要進行通信的任務使用P、V兩個操作對信號量進行處理。P操作使信號量的值減1,V操作使信號量的值加1。

按照用途不同,信號量可以分為互斥信號量、二進制信號量、計數信號量3種。

(1)互斥信號量

互斥信號量用于解決互斥問題。它有0和1兩個值,初始狀態下信號量的值被設置為1,表示目前沒有任務處在臨界區之中。當某一任務進入臨界區后,互斥信號量的值被設置為0,此時如果再有其他的任務想進入臨界區就只能等待。處在臨界區中的任務退出臨界區后,互斥信號量的值將被重新設置為1,允許其他的任務進入臨界區。使用互斥信號量時需要特別注意,它可能會引起優先級反轉。

(2)二進制信號量

二進制信號量用于解決同步問題。它也有0和1兩個值,初始狀態下其值被設置為0,表示同步的對方尚未達到指定的同步點(如計算任務尚未產生出計算結果),信號量的請求方(如打印任務)必須等待。當同步的對方達到指定的同步點后(如計算任務已經產生出計算結果),二進制信號量的值被設置為1,此時信號量的請求方將可以繼續往下執行(如打印計算結果)。

(3)計數信號量

計數信號量與二進制信號量有些相似,但它不僅僅有0和1兩個值。它的值代表了系統中某種資源的數量,其初始值一般與資源的數量相等。這種信號量經常用于控制對多個共享資源的使用。如解決生產者和消費者問題。

2. 事件

事件通過發出系統中某些狀態已經發生變化的通知來進行任務通信。從嵌入式操作系統使用者的角度看,一個事件就是一個標志,用于表示系統中的某一狀態是否已經發生變化。多個事件可以構成一個事件集,一個事件集可以用一個無符號整數來表示(如用一個32位的無符號整數)。每個事件在這個無符號整數中用一位來代表。用事件進行同步時有兩種情況:如果一個任務在等待一個事件集中的任意一個事件發生,稱為邏輯“或”關系的事件同步;如果一個任務在等待一個事件集的所有事件發生,稱為邏輯“與”關系的事件同步。

事件主要用來實現任務間的同步。它與其他同步機制之間的一個顯著區別是,可以實現一個任務與多個任務(或中斷服務程序)之間的同步。

通過事件進行同步的任務需要保存等待它處理的事件集。當有其他的任務或中斷服務程序向它發送事件時,該任務將進行如下的處理:按照“或”或者“與”的關系判斷任務等待的事件是否都已經發生,如果已經發生,那么任務轉為就緒狀態;否則,將新到達的事件保存到任務的待處理事件集中,任務繼續等待事件,直到所有的事件發生為止。需要注意:事件沒有隊列。在向一個任務多次發送同一事件時,如果該任務未進行處理,只等效于發送一次。

3. 信號

信號(Signal)也稱異步信號。它是UNIX中最早的任務通信機制之一,一些嵌入式操作系統也支持這種通信機制(如DeltaOS)。信號主要用于實現任務與任務之間、任務與中斷服務程序之間的同步。一個任務(或中斷服務程序)可以使用信號通知同步方某件事情已經發生。

向任務發送信號使用操作系統提供的系統調用。任務接收和處理信號通過任務的信號服務程序實現。需要接收和處理信號的任務由兩部分組成,一部分是任務的主體,另一部分是任務的信號服務程序。當有其他的任務或中斷服務程序向某個任務發送一個信號時,如果該任務正在運行,那么它將中止運行任務的主體,轉而運行任務的信號服務程序;如果該任務當前沒有在運行,那么此后輪到該任務運行時,也將首先執行任務的信號服務程序。如果一個任務沒有定義信號服務程序,那么向其發送信號沒有意義。

一個信號可以被響應,也可以被屏蔽。只有在一個任務可以響應某信號時,向該任務發送這一信號,收到信號的任務才會運行其信號服務程序。

信號與事件有某些類似之處。它們都可以表示某個事情已經發生,但事件的處理方式是同步的,而信號的處理方式是異步的。事件的處理方式是同步的指:對于一個任務來說,在什么地方接收和處理事件是已知的,完全取決于任務的代碼。信號的處理方式是異步的指:任務不能夠預知在什么時候會接收到一個信號,它只是注冊了一個信號服務程序,一旦有其他任務或中斷服務程序向其發送信號,只要信號沒有被屏蔽,接收到信號的任務就會中止運行其主體,轉去執行信號服務程序。

4. 消息郵箱

消息郵箱簡稱郵箱,它實際上是內存空間的一個緩沖區。消息郵箱用于實現任務與任務之間、任務與中斷服務程序之間的同步和數據交換。

每個消息郵箱有一個阻塞隊列(也可以所有資源共用一個阻塞隊列)。在消息郵箱只能存放一個消息,如果在一個消息郵箱中已經有消息時還要繼續向它發送其他的消息,則這一操作失敗。一個消息只能讀取一次,消息被讀出后,消息郵箱將變為空郵箱。在消息郵箱為空時讀取消息將導致實施這一操作的任務被阻塞,其任務控制塊將被放到郵箱的阻塞隊列中。如果有多個任務在等待消息,當消息到來時只能有一個任務獲得消息,其他任務仍將繼續等待。

5. 消息隊列

消息隊列簡稱隊列??梢哉J為它是一個其中可以容納多個消息的郵箱。除了這一點之外,隊列與郵箱的作用及其操作基本相同。

6. 共享內存

共享內存是一種通過內存中的公用區進行任務通信的方式,很適合在任務之間進行數據交換。在通用操作系統中(如UNIX System V)可以通過API定義某一內存區域為共享區域。發送數據的任務和接收數據的任務通過對共享區域中同一位置進行讀、寫實現任務間的通信。由于嵌入式操作系統所采用的內存管理機制一般都比較簡單,所以共享內存變得更加容易。有相當一部分嵌入式操作系統的任務是共存于單平面的線性地址空間中,處于用戶態的應用任務之間可以直接相互訪問對方的數據。

共享內存雖然是一種簡單、高效的任務通信機制,但是采用這種通信方式時操作系統只提供了一個基本的渠道,通信過程中遇到的許多問題都要通信雙方自己管理和解決。主要問題包括:

① 通信雙方要約定一個統一的數據結構,否則將在通信過程中出現混亂;

② 讀、寫共享內存區的代碼段是一個臨界區,需要采用某一種機制(如信號量)來解決互斥問題。

7. 管道

管道是UNIX中的一種傳統任務通信方式。它通過文件實現任務間的通信(實際上是用內存模擬文件的存儲介質),因此可以認為,管道是一種共享文件的任務通信方式。管道可用于實現任務間的同步,也適合于在任務之間進行數據交換。

在UNIX系統中,利用建立管道的接口函數(函數名為pipe)可以返回兩個文件描述符,一個用于向管道中寫,另一個用于從管道中讀。發送數據的任務視管道為輸出文件,接收數據的任務視管道為輸入文件。管道中的數據是一種非結構的字節流,沒有優先級,完全按照FIFO的方式在管道中傳輸。當管道空時,從管道中讀數據的任務將被阻塞;當管道滿時,向管道中寫數據的任務將被阻塞。

在嵌入式操作系統中,實現和使用管道的方法與經典的UNIX系統有一些區別。例如,在VxWorks操作系統中建立管道的接口函數(函數名為pipeDevCreate)并不直接返回用于讀、寫管道的文件描述符,而是需要在建立管道之后,再用其他的接口函數打開管道(打開管道的方法和打開普通文件的方法一致,都是使用名字為open的函數),并同時返回一個文件描述符。這個描述符既可以用來從管道中讀,也可以用來向管道中寫。

2.2.3 內存管理

內存是嵌入式系統的一種重要資源,內存管理是嵌入式操作系統的一項基本功能。各種嵌入式操作系統提供的內存管理功能雖然有很大的差別,但都會在一定程度上提供一些內存管理方面的功能。內存管理的功能一般由嵌入式操作系統的內核來實現。

1. 嵌入式操作系統內存管理技術的特點

嵌入式操作系統在內存管理方面追求的目標是簡潔高效。與通用操作系統相比,嵌入式操作系統所采用的內存管理技術有以下幾個顯著特點。

(1)很多嵌入式操作系統不支持程序動態裝載

嵌入式系統專用性很強,一個特定的嵌入式系統只需要運行有限個固定的應用程序。在很多嵌入式系統中,這些應用程序都是由嵌入式系統的生產者事先固化在內存之中,并以現場執行(eXecute In Place,XIP)的方式運行,根本不需要在系統運行的過程中從磁盤存儲器或者其他的什么地方裝載到內存中。

(2)大多數嵌入式操作系統不支持虛擬存儲

嵌入式操作系統不支持虛擬存儲除了有不支持程序動態裝載的原因之外,還有另外兩個原因。一個是嵌入式系統上面一般不帶有磁盤,因而需要換出的頁面無處存放;另一個是嵌入式系統都有較強的實時要求,而存儲頁面的換入與換出是對實時性極大的破壞,一個本來很快就可以完成的處理過程,可能會因為某個子程序或變量所在的存儲頁面恰好不在內存而需要從外部設備換入,因而需要進行一系列復雜的操作,結果導致程序的執行時間變得難以確定。

(3)高端嵌入式操作系統和低端嵌入式操作系統支持的內存管理功能相差很大

雖然多數的嵌入式操作系統不支持虛擬存儲和程序動態裝載,但隨著嵌入式軟件的應用與技術的發展,出現了兩種傾向。一方面,個別高端的嵌入式操作系統已經開始支持虛擬存儲、程序動態裝載等內存管理技術。這些高端嵌入式操作系統采用的內存管理技術已經很復雜,與Linux、Windows等操作系統基本在同一個數量級之上。另一方面,一些資源極其受限的嵌入式系統(如無線傳感網的結點)也開始采用嵌入式操作系統。在這些低端嵌入式操作系統上內存管理功能幾乎趨近于0。使用這種嵌入式操作系統時,應用程序的存儲地址需要由開發者在開發階段用定址工具指定,并在運行前將應用程序裝入到對應的存儲器中當中去。在運行的過程中應用程序往往也不能動態地請求分配內存。

2. 嵌入式操作系統常用的內存管理技術

高端嵌入式操作系統和低端嵌入式操作系統采用的內存管理技術雖然相差很大,但采用復雜內存管理技術(如可以支持虛擬存儲)的嵌入式操作系統實際很少。大多數嵌入式操作系統采用的還是比較簡潔的技術。根據是否支持在運行過程中動態地分配內存,可以把嵌入式操作系統所采用的內存管理技術分為兩類:一類是靜態內存管理技術,另一類是動態內存管理技術。

(1)靜態內存管理技術

采用靜態內存管理技術時必須在系統運行前為所有的任務分配它們所需要的內存,任務在運行過程中不能再請求分配新的內存,也無法支持程序的動態裝載。這種內存管理技術有以下一些特點:

①實現簡單。在不支持動態重定位的情況下,內存的分配完全在系統運行之前完成,嵌入式操作系統基本不承擔任何工作。在支持動態重定位的情況下,嵌入式操作系統承擔的工作也很少。因此這種內存管理技術實現簡單,能夠顯著減少嵌入式操作系統的代碼量和復雜程度。

② 實時性能高。由于靜態內存管理技術實現簡單,所以各種內存操作所需的時間都很確定,都能夠在可預期的時間內完成。相比之下,采用動態內存管理技術時,如果用戶請求分配的內存得不到滿足,通常會使發出請求的任務進入阻塞狀態,在很長的時間內不能被運行。

③ 易于在沒有MMU的處理器上實現。采用靜態內存管理技術的嵌入式操作系統,一般沒有必要支持動態重定位,不需要利用MMU進行從邏輯地址到物理地址的轉換,因此比較易于在沒有MMU的處理器上實現。相比之下采用動態內存管理技術的嵌入式操作系統在沒有MMU支持的情況下,實現難度很大。但是,通過MMU對內存進行管理也有許多好處,如可以顯著提高系統的安全性。在不使用MMU的情況下,系統中所有的應用程序都處在一個單平面的線性地址空間中。應用程序之間可以隨意相互訪問,嵌入式操作系統無法截獲內存的異常訪問,因而也無法防止程序間的無意和有意破壞。

④ 編程靈活性差:由于在運行過程中任務不能動態地請求分配新的內存,所以大大降低了編程的靈活性。

靜態內存管理技術雖然有明顯的缺點,但由于它的簡潔高效的優點,仍然被很多嵌入式操作系統采用,特別是那些實時性要求高、應用程序比較簡單、任務數相對固定的嵌入式操作系統。

(2)動態內存管理技術

采用動態內存管理技術時,在系統運行的過程中操作系統可以根據需要為任務分配新的內存。這種內存管理技術的存儲空間典型組織結構如圖2.19所示。低端存儲區中存儲中斷向量表、系統引導程序和引導參數。操作系統區中存儲操作系統的靜態代碼和數據。應用程序區中存儲應用程序的代碼和數據。這些代碼和數據的尺寸可以在編譯、鏈接期間確定。應用程序和操作系統在運行過程中動態申請的內存空間在動態存儲區中分配。

圖2.19 存儲空間的典型組織結構

由于低端存儲區、操作系統區、應用程序區等區域的大小在系統運行前就可以確定,因此用很簡單的方法就可以進行管理。相對來說,動態存儲區的管理要麻煩許多。嵌入式操作系統管理動態存儲區的技術主要有3種:單一區、堆、分區。

1)單一區

采用單一區方式管理動態存儲區時整個動態存儲區被當作一個整體,并用一定的數據結構對其進行管理,比較常見是用鏈表。采用鏈表對動態存儲區進行管理時,動態存儲區中的可用內存塊用鏈表指針鏈接在一起,形成一個可用內存鏈。每一塊可用內存是鏈表中的一個結點。由于每塊內存的大小不等,所以要在每個可用內存塊中保存兩個信息,一個是本內存塊的大小,另一個是指向下一個可用內存塊的指針。當用戶申請分配內存時,按照一定的算法(如首次擬合法、最佳擬合法)在可用內存鏈中找到一塊可以滿足用戶需要的內存,并將該內存塊或者其中的一部分分配給用戶。如果是將內存塊的一部分分配給了用戶,則需要把其余的部分鏈接到可用內存鏈中。例如,若某一嵌入式系統的動態存儲區的大小是4096KB,那么與系統初始狀態相對應的可用內存鏈如圖2.20所示。如果此后用戶依次申請了大小分別為32KB和128KB的兩塊內存,然后又釋放了大小為128KB的內存,可用內存鏈的變化情況依次如圖2.21、圖2.22、圖2.23所示。

圖2.20 與系統初始狀態相對應的可用內存鏈

圖2.21 申請32KB內存后的可用內存鏈

圖2.22 再申請128KB內存后的可用內存鏈

圖2.23 釋放128KB內存后的可用內存鏈

可以看到,隨著系統的運行會在動態存儲區中出現很多內存碎片,使內存的使用效率顯著降低。例如,可能會出現即使內存數量可以滿足用戶要求的情況下,由于沒有一塊足夠大的連續內存,而無法滿足用戶的內存分配請求。某些嵌入式操作系統提供垃圾回收功能,通過對內存數據的移動和重新組合來解決內存碎片問題。但垃圾回收時系統開銷很大。實時性要求高的嵌入式操作系統不適用這種方法。

單一區方式管理動態存儲區時,嵌入式操作系統必須提供兩個基本的系統調用:一個是分配內存的系統調用;另一個是釋放內存的系統調用。

2)堆

堆是一塊連續、大小可配置的內存空間。在這個空間中可以按可變的尺寸向用戶分配內存。如圖2.24所示,采用堆方式對動態存儲區進行管理時,在動態存儲區中可以建立若干個堆。在每一個堆中可以用與前面所介紹的鏈表類似的數據結構對內存塊進行管理。

堆方式管理動態存儲區時,用戶動態申請內存和釋放內存時都在某一個堆中進行,所以內存碎片會限制在一個堆中,而不是散布在整個動態存儲區中,通過刪除堆就可以消除在運行過程中產生的內存碎片。所以采用堆方式對動態存儲區進行管理可在一定程度上彌補單一區方式的不足。

為了實現對堆的操作,嵌入式操作系統通常提供以下系統調用:

① 建立堆;

② 刪除堆;

③ 從堆中分配內存;

④ 把內存釋放回堆中;

⑤ 擴展堆;

⑥ 獲得堆的信息。

3)分區

圖2.24 采用堆方式對動態存儲區進行管理

圖2.25 分區方式管理動態存儲區

分區是一塊連續的內存空間,它由若干大小相同的內存塊組成。如圖2.25所示,分區方式管理動態存儲區時,在動態存儲區中可以建立若干分區。各個分區大小不同,每個分區里各包含數量不等的內存塊。各分區中的內存塊的大小也不相同。對于分區中的可用內存塊也可以用鏈表管理,但由于同一個分區中的內存塊大小都相同,所以在可用內存塊中只需保存指向下一個可用內存塊的指針,而不必保存本內存塊的大小。

當有用戶請求分配內存時,嵌入式操作系統的內存管理模塊將比較每個分區中內存塊的大小,從內存塊的尺寸大于并最接近用戶請求值的分區中分配一個內存塊給用戶。用戶釋放內存時,則把用戶釋放的內存塊放回原來的分區中,這樣就可以避免出現內存碎片。

分區方式也有缺點。由于用戶申請分配的內存的尺寸與分區的尺寸不可能完全相同,因此在分區內部還是會造成一定的浪費,但是這與形成大量的內存碎片相比,整體效果會好許多。

為了實現對分區的操作,嵌入式操作系統通常提供以下系統調用:

① 建立分區;

② 刪除分區;

③ 從分區中分配內存;

④ 把內存釋放回分區中;

⑤ 獲得分區的信息。

不管采用哪種方式管理動態存儲區,如果讓應用程序在運行的過程中,直接使用操作系統的系統調用請求分配新的內存并不合適,其原因如下:

① 通過操作系統的系統調用動態申請分配內存空間時,要在用戶態和核心態之間進行切換,與單純處于用戶態的程序相比,系統開銷相對較大。

② 不論采用哪種方式管理動態內存,嵌入式操作系統對最小的內存分配單位都有一定的限制,必須以物理頁為單位,所以在用戶需要分配較小的內存時,總會造成較大的浪費。

③ 通過操作系統的系統調用動態申請分配內存空間時,如果申請分配內存的請求不能被滿足,發出請求的任務將被阻塞,這將極大地影響任務的實時性能。

為了解決上述問題,嵌入式系統一般是通過庫函數來實現動態內存的分配。這種庫函數預先分配一塊很大的內存,當有用戶請求分配內存時就從這塊預先分配的內存中拿出一塊來給用戶使用。在預先分配的內存都使用光之后,庫函數會再向操作系統請求分配一大塊內存。這種通過庫函數解決動態內存分配問題的方法,比起直接通過操作系統的系統調用的方法效率可以顯著提高。

3. 邏輯地址到物理地址的轉換

在編寫嵌入式系統的應用程序時,開發者都是使用邏輯地址,但應用程序在執行時必須放在物理存儲器中,使用物理地址。那么邏輯地址到物理地址的轉換在何時進行?又怎樣進行呢?對于這個問題,有兩種不同的解決途徑:一種是采用靜態重定位的方法,在應用程序執行之前進行從邏輯地址到物理地址的轉換;另一種是采用動態重定位的方法,在應用程序執行的過程中進行從邏輯地址到物理地址的轉換。一些嵌入式操作系統不支持動態重定位,如果應用程序是在這樣的嵌入式操作系統上面運行,那么就必須在應用程序的開發階段利用定址工具為應用程序指定物理地址,然后把應用程序裝入到對應的物理內存中去。還有一些嵌入式操作系統能夠支持動態重定位,如果應用程序是在這樣的操作系統上面運行,那么就不需要在開發階段為應用程序指定物理地址,邏輯地址到物理地址的轉換可以在應用程序執行的過程中在嵌入式操作系統的協助下自動完成。

嵌入式操作系統實現動態重定位需要MMU(Memory Management Unit)的支持,MMU是CPU中一個用于管理存儲器的邏輯單元。目前很多CPU中都包含這種邏輯單元。MMU有兩個主要作用,第一,將邏輯地址映射為物理地址;第二,實現內存訪問控制,檢查是否有越界訪問和越權訪問的情況發生,并在發生此類情況時產生異常。

MMU有多種功能模式,不同MMU支持的功能模式雖然不太相同,但通常都支持以下幾種類型的功能模式:

① 單平面模式:在這種模式下包括操作系統在內的所有程序共存于一個單平面的線性地址空間中,每個程序都可以對整個內存空間進行訪問。

② 保護模式:在這種模式下每個應用程序只能訪問屬于自己的內存區域,不能訪問屬于其他應用程序的內存區域,但應用程序的邏輯地址與物理地址間的映射關系在應用程序運行之前已經確定,并且在應用程序運行的過程中固定不變。

③ 虛擬內存模式:在這種模式下每個應用程序只能訪問屬于自己的內存區域,不能訪問屬于其他應用程序的內存區域,而且應用程序的邏輯地址與物理地址間的映射關系可以在應用程序運行的過程中發生變化。

使用MMU的保護模式就可以支持動態重定位。如果打算支持虛擬存儲器,則需要使用虛擬內存模式。

采用MMU管理內存時,物理內存被分為一些大小相等的物理頁,應用程序的邏輯地址空間也被劃分為同樣大小的邏輯頁。為了便于處理,頁的大小總是2的整數次冪。對于大多數MMU來說,頁的典型尺寸是4KB。這樣,邏輯地址和物理地址都可以分為頁號和頁內地址兩部分。例如,對于一個字長為32位的嵌入式系統(有4GB的邏輯地址空間),如果頁的大小是4KB,那么32位的邏輯地址就被分成了由20位組成的頁號和由12位組成的頁內地址兩部分。在采用了分頁的內存管理機制后,操作系統給應用程序分配內存時,總是以頁為單位,并且分配給一個應用程序的內存不必連續。通過MMU提供的頁表就能夠實現邏輯地址到物理地址的轉換。如圖2.26所示,假設有一個大小為10KB的應用程序被裝入到了物理內存的第2、第5和第8頁中。如果在程序運行的過程中要對邏輯地址4351(0x000010FF)進行訪問。那么這個邏輯地址首先被分成邏輯頁號1和頁內地址255,然后以邏輯頁號為索引檢索頁表可得到存儲該頁的物理內存的頁號5,最后將物理頁號和頁內地址結合在一起就可以形成訪問物理內存的地址。

圖2.26 動態地址轉換過程

2.2.4 文件管理

文件管理并不是嵌入式操作系統必須提供的一種功能。在實際的嵌入式操作系統中對于文件管理會有3種情況:

① 不支持文件系統;

② 為了設備管理的需要只支持文件系統的一些基本機制,如文件描述符表,而不支持實際的文件存儲;

③ 支持某種或某幾種文件系統。在支持文件系統的情況下,一般是由操作系統內核之外的部分來實現文件管理的功能,而且由于在嵌入式系統上很少有磁盤之類的存儲設備,所以嵌入式操作系統的文件系統也不是在通用計算機系統上普遍采用的磁盤文件系統。嵌入式操作系統的文件系統比較多的是采用RAM、ROM和閃存做文件的存儲介質。

下面介紹幾種適用于嵌入式系統的文件系統。它們是JFFS/JFFS2文件系統、YAFFS文件系統、CRAMFS文件系統、ROMFS文件系統、RawFS文件系統、RAMFS文件系統、TmpFS文件系統、TSFS文件系統、DOSFS文件系統。

1. JFFS/JFFS2文件系統

JFFS(Journaling Flash File System)是瑞典的Axis公司開發的一種專門針對閃存的文件系統。Red Hat公司對JFFS進行了改進,形成了JFFS2。在以閃存做存儲介質時,采用JFFS和JFFS2可以獲得較高的效率。JFFS和JFFS2也是一種日志型的文件系統。日志文件系統相對于普通的文件系統最主要的特點是增加了日志記錄。它進行文件管理時的一個重要原則就是必須先寫日志后寫數據。

(1)閃存的類型與特點

閃存(Flash Memory)又稱PEROM(Programmable and Erasable Read Only Memory),是20世紀80年代末出現的一種存儲器件。它有NOR、NAND、AND、DiNOR等多種類型,其中最常用的是NOR型和NAND型。不論什么類型的閃存都有兩個基本的特點:

① 在掉電情況下數據不會丟失,并且可以在線寫入;

②芯片的寫入次數有一定的限制(一般為10萬至100萬次)。超過這個限制后芯片就會發生損壞。

NOR型閃存適合于存儲程序代碼。其特點是支持現場執行(eXecute In Place,XIP),即程序可以直接在閃存上面運行,而不必讀到RAM之中。原因是NOR型閃存有足夠的地址引腳來尋址,可以很容易地存取閃存中的每一個字節。

NAND型閃存適合于存儲純數據和文件,主要用來做SM(Smart Media)卡、CF(Compact Flash)卡、PCMCIA(Personal Computer Memory Card International Association)卡。相對于NOR型閃存,NAND型閃存的存儲容量更大,并且寫入和擦除的速度也更快。但NAND型閃存的存儲單元(字節)不可以直接改寫,如果需要對某一數據進行改寫,必須以塊為單位。一般是512字節為一塊。這一點與硬盤相似。因此在存取NAND型的閃存時有兩種不同的方法。

①直接訪問閃存。就是直接對閃存進行讀寫。由于NAND型的閃存必須按塊寫入,所以進行寫入操作并不像寫普通的RAM那樣簡單。如果要在某字節中寫入數據,必須先把該字節所在的存儲塊中的數據全部讀出,然后擦除存儲塊中的數據,最后再把數據整塊地寫入。進行這些操作像往一個設備中進行輸出那樣需要用一個驅動程序來完成,而不是僅憑一條指令就能實現。

② 通過接口訪問閃存。有些NAND型的閃存上帶有支持IDE接口的電路,對于這樣的閃存可以像訪問普通硬盤那樣讀寫。這種閃存的優點是原來磁盤設備上的文件系統可以方便地在閃存上使用。但是由于閃存與普通IDE設備畢竟有很多的不同之處,所以按照與IDE設備相同的方法訪問閃存,數據的可靠性往往得不到保障。此外,使用這種訪問方法必須有專門的電路支持,因為并不是所有的閃存都提供這種支持,所以不是總能夠采用這種方法訪問閃存。

(2)閃存文件系統的特殊要求

由于閃存在結構和操作方式上與硬盤、EPROM等其他存儲介質有較大區別,所以使用閃存做文件系統的存儲介質時必須對文件系統進行特殊的設計,以保證系統的性能達到最優。以閃存作為存儲介質的文件系統一般要考慮以下問題。

① 掉電安全:嵌入式系統的運行環境一般比較惡劣,這對于閃存文件系統提出了較高的要求。無論程序崩潰或系統掉電,都不應該影響文件系統的一致性和完整性。

② 均衡磨損:由于閃存芯片的寫入次數是有限制的,所以為了避免某一存儲塊在其他存儲塊之前早早地達到磨損極限值,從而導致整個芯片損壞,文件系統在使用閃存時應盡量使每一個存儲塊經歷大致相同的擦寫次數。也就是說,應當讓整個芯片“均衡地磨損”,這樣可相對延長整個芯片的使用壽命。

③ 碎片回收:系統運行了一段時間之后,在存儲文件的閃存上會出現很多“碎片”。為了保證存儲空間的使用效率,必須對碎片進行回收。閃存(NAND型)的寫入和擦除是以塊為單位進行的,因此碎片回收也應以塊為單位。

④ 存儲空間消耗:這里的存儲空間消耗指文件系統管理結構所占的存儲空間。由于嵌入式系統中的存儲空間一般很有限,所以減少文件系統管理結構所占的存儲空間很有意義。

(3)JFFS/JFFS2文件系統的存儲、加載和操作

在JFFS/JFFS2文件系統中,一個文件由一個或多個結點組成。如圖2.27所示,每一個結點中包含元信息和文件數據兩部分內容。元信息所包含的內容有結點版本號、索引結點信息、數據在文件中的位置、指示結點是否已廢棄的標志等。

圖2.27 JFFS/JFFS2文件系統的結點所包含的內容

JFFS/JFFS2文件系統在使用前必須先進行加載。加載JFFS/JFFS2文件系統時,整個文件系統會被掃描一次,然后根據各個結點中的元信息在內存中生成一個文件系統的目錄樹,同時也自動生成一個記錄文件在閃存中物理存儲位置的地址表。

讀取文件內容時,利用加載文件系統時生成的目錄樹和地址表就可以輕松地找到需要的數據。刪除文件時采用的方法是在結點的元信息中將結點標志成已廢棄狀態。修改文件的數據時,只是先將包含新數據的結點寫到閃存空閑空間的開始處,然后再把包含舊數據的結點標志為廢棄結點,并不直接修改原有的結點。

(4)JFFS/JFFS2文件系統的回收機制

隨著寫入和修改操作不斷進行,閃存上的存儲空間會逐漸被用光。這時文件系統就需要對已廢棄結點所占據的空間進行回收,以便對它們重新加以利用?;厥者^程如圖2.28所示。每次回收時,系統將從第一個結點開始進行分析。如果該結點已廢棄,則將它跳過,并開始分析下一個結點。如果該結點中的數據仍然有效,即結點仍然被占用,則把其中的數據寫到閃存空閑空間開始處的一個新結點中,并將這個結點設置為已廢棄狀態。按照這種方式回收工作不斷往前推進,直到一個完整的存儲塊被廢棄為止。這時這個存儲塊將被加入到空閑空間的尾部,等待重新被使用。當整個閃存都被掃描一遍之后,回收工作便結束。從上述回收過程可以看出,為了保障回收的正常進行,必須在閃存上留有一些工作空間,而不能等到閃存上面的存儲空間真正被全部用光時才進行回收。

圖2.28 廢棄結點的回收過程

這種回收過程先寫入新結點,然后擦除舊結點,在閃存上線性地推進,使每個存儲塊都保持了基本相同的擦除次數,實現了“均衡磨損”整個閃存芯片的要求。但是當某個存儲塊全部為有效結點時,擦除工作依然不可避免,這意味著會進行許多不必要的工作。

2. YAFFS文件系統

YAFFS(Yet Another Flash File System)與JFFS/JFFS2類似,也是一種專門為NAND型閃存設計的文件系統。

YAFFS文件系統有以下的一些特點。

① YAFFS是日志型的文件系統,在系統出現故障時,可以根據日志來保證數據的完整性。

② 實現了存儲介質的“均衡磨損”??梢允归W存中的每一個存儲塊經歷大致相同的擦寫次數。

③ 掉電保護??梢杂行У乇苊庀到y意外掉電對文件系統一致性和完整性造成破壞。

④ 采用層次化的文件結構。YAFFS文件系統是按層次結構設計的,分為文件系統管理層、內部實現層和NAND接口層3個層次。這樣就使系統的結構很清晰,可以方便地集成到不同的操作系統當中去。

⑤ 系統精簡。與JFFS/JFFS2文件系統相比,YAFFS減少了一些相對不重要的功能,因此速度更快,占用存儲空間更少。

3. CRAMFS文件系統

CRAMFS(Compress RAM File System)是一種適合以RAM和ROM做存儲介質,并在嵌入式系統上使用的文件系統。

在嵌入式系統上使用CRAMFS文件系統有其明顯優勢。通用計算機有時也會使用RAM或ROM做文件的存儲介質,所謂的RAMDISK就是其中之一。RAMDISK用RAM來模擬硬盤存儲器,從而可以在RAM上保存文件,這大大提高了文件的訪問速度。如果將某些需要頻繁訪問的文件放在RAMDISK上,整個系統的效率會顯著提高。但在嵌入式系統中直接采用與RAMDISK類似的方法很難行得通。嵌入式系統通常采用閃存做外存,與硬盤相比,閃存的容量要小得多。在嵌入式系統中,內存更是有限的資源。如果把文件系統放在RAMDISK上,就需要在系統開始運行之后,首先把外存(如NAND型閃存)上的文件解壓縮到內存中,構造起RAMDISK環境,文件系統才可以正常工作。因此要求同樣的數據不僅在外存中占據空間(以壓縮的形式存在),而且還在內存中占用更大的空間(以解壓縮后的形式存在)。這對內存和外存都非常有限的嵌入式系統來說是難以接受的。

CRAMFS文件系統在一定程度上解決了RAMDISK技術的缺陷。它是一個壓縮形式的文件系統,并不需要一次性地將文件系統中的所有數據都解壓縮到內存中,而只是在系統需要訪問某個數據時,才計算出該數據在CRAMFS文件系統中的位置,并將其解壓縮到內存中,然后通過對內存的訪問來獲取需要讀取的數據。

CRAMFS文件系統雖然解決了單純采用RAMDISK技術所帶來的缺陷,但也有一些不足之處,主要體現在以下兩方面。

① 采用實時解壓縮方法雖然節約了存儲空間和不必要的解壓縮過程,但也給使用者帶來了更多的延遲。

② 其中的數據都是經過處理、壓縮的,執行寫操作有很多麻煩。因此為了簡單,CRAMFS文件系統不支持寫操作。

4. ROMFS文件系統

ROMFS(ROM File System)是一種只讀文件系統,適用于ROM或閃存做存儲介質。它經常在嵌入式系統中作為根文件系統,用于保存系統的引導程序。

ROMFS占用的系統資源相對很少,比其他文件系統更加節省存儲空間。這一方面由于ROMFS文件系統需要的代碼很少,另一方面ROMFS文件系統相對簡單,因此文件系統的元數據(如文件系統的超級塊)需要較少的存儲空間。

ROMFS文件系統還有一個顯著的優點,這就是它按順序存放文件的所有數據,所以支持程序以現場執行(eXecute In Place,XIP)的方式運行,即程序可以直接在存儲文件的ROM或閃存內運行,而不必裝入到RAM之中,因此可以節省可觀的RAM空間。

5. RawFS文件系統

RawFS(Raw File System)文件系統是一種元數據最小化的文件系統,結構非常簡單。它將整個磁盤的存儲空間當作一個文件。讀寫磁盤時根據字節偏移量來確定讀寫哪一部分內容。在不需要進行復雜操作時,RawFS文件系統在空間占有量和操作性能上都有很大的優越性。

6. RAMFS文件系統

RAMFS(RAM File System)文件系統以RAM做存儲介質,輸入/輸出操作的速度非常快,所有讀寫操作幾乎可以在瞬時完成,適合于存儲臨時文件或者頻繁改動的文件。

RAMFS文件系統有一個顯著的優點,這就是文件系統的大小可以隨文件與文件目錄的大小變化,因而能夠比較理想地使用內存空間,避免不必要的浪費。但由于RAMFS文件系統的文件是放在RAM中,因此在系統斷電后文件系統的所有數據都將丟失。

7. TmpFS文件系統

TmpFS(Temporary File System)文件系統與RAMFS文件系統類似,也是將文件存儲在RAM上。兩者的主要功能和特點也有很多相同之處。例如,文件系統的大小會隨著文件的大小發生變化等。不同的是,TmpFS文件系統不像RAMFS文件系統那樣,將文件建立在物理內存上,而是向虛擬存儲系統請求頁面來存儲文件。因此使用TmpFS文件系統需要虛擬存儲系統的支持。

8. TSFS文件系統

TSFS(Target Server File System)是VxWorks操作系統的一種文件系統。與其他的文件系統不同,它的文件存儲在宿主機上。在進行軟件開發和對目標機進行診斷時可以使用這種文件系統。

TSFS文件系統通過網絡或通信端口進行遠程文件輸入/輸出操作。在VxWorks操作系統開發環境中,宿主機與目標機之間的關系如圖2.29所示,目標機與宿主機之間的通信由目標代理和目標服務器共同負責。目標代理是VxWorks操作系統中的一個任務,目標服務器則是宿主機上的一個任務。在進行文件操作時,TSFS文件系統通過目標代理從VxWorks操作系統的輸入/輸出系統向宿主機發送輸入/輸出請求。宿主機上的目標服務器使宿主機上的文件系統處理這個請求,完成嵌入式系統所要進行的文件操作。例如,當嵌入式系統中的應用程序調用open()函數在TSFS文件系統中打開一個文件時,該文件實際位于宿主機之上。應用程序利用open()函數返回的文件描述符進行的讀寫操作都是對宿主機上的文件進行操作。

圖2.29 VxWorks開發環境中宿主機與目標機之間的關系

9. DOSFS文件系統

DOSFS文件系統原先是用在MS-DOS操作系統上的文件系統。這種文件系統適合建立在塊設備(如磁盤)上面。由于它的結構相對比較簡單,所以也常用在嵌入式操作系統中。

2.2.5 中斷管理

中斷管理是嵌入式操作系統應當提供的一項基本功能。對于實時嵌入式操作系統來說,這一功能更是必不可少。通過中斷機制可以確保具有實時特性的任務能夠及時執行。中斷管理功能主要由嵌入式操作系統的內核來實現。

1. 中斷的種類

從廣義上講,中斷指在計算機執行期間,由于某種預期或非預期事件的發生,導致程序的正常執行流程發生改變的過程。它分為硬中斷、自陷和異常三類。從狹義上講,中斷僅指硬中斷。在本書中如不特別說明,中斷一詞均指狹義的概念。

中斷:中斷由來自CPU外部的事件引起。它是一種由于CPU外部原因而使程序的正常執行流程發生改變的過程。引起中斷發生的事件稱為中斷請求。中斷可能在程序執行的任何位置發生,發生中斷的時間也不確定。使用中斷的目的在于提高系統效率,使計算機系統在進行輸入/輸出操作的同時,CPU仍然能夠繼續執行正常的程序。中斷屬于異步過程,而自陷和異常則為同步過程。

自陷:自陷也叫軟中斷。它通過CPU的軟件指令產生。因此,產生自陷的時機是預知的,可根據需要在程序中進行設定。通過自陷指令,可以使CPU執行的程序流程發生變化,轉去執行特定的程序。Motorola 68000中的Trap指令、ARM中的SWI指令、Intel 80x86中的INT指令都是可以產生自陷的指令。自陷是一種非常重要的機制,通過該機制可以在用戶模式下執行系統模式下的操作。操作系統的系統調用就是借助于自陷實現的。

異常:與自陷不同,異常沒有對應的處理器指令,它可以被認為是一種CPU自動產生的自陷,以便來處理異常事件。如0做除數、執行非法指令和內存越界訪問等。當異常事件發生時,處理器也需要無條件地暫停運行當前的程序,轉去執行特定的處理程序。

2. 中斷處理的過程

計算機系統響應和處理一個中斷的全過程分為中斷檢測、中斷響應和中斷處理三個階段。前兩個階段由硬件來完成。第三個階段則由操作系統和應用軟件來完成。

(1)中斷檢測階段

中斷檢測在每條指令結束時進行。檢測內容包括是否有中斷請求信號和是否滿足產生異常的條件。如果沒有中斷請求信號,產生異常的條件也不滿足,則處理器繼續運行,并通過取指令周期取當前程序的下一條指令;如果有中斷請求或產生異常的條件被滿足,則進入中斷響應階段。

(2)中斷響應階段

在中斷響應階段計算機系統的硬件一般要完成以下工作:

① 復位引起中斷的中斷請求信號;

② 保存某些關鍵寄存器的內容,將它們壓入堆棧中;

③ 禁止可屏蔽中斷和單步異常;

④ 獲得中斷向量號,根據中斷向量號,查找中斷向量表,根據保存在中斷向量表中的中斷服務程序地址轉移到中斷服務程序去執行。

(3)中斷處理階段

中斷處理階段的工作由中斷服務程序和中斷服務任務配合完成。中斷服務程序是嵌入式操作系統內核的一部分,中斷服務任務是內核之外的一種特殊任務。雖然許多中斷服務任務由嵌入式操作系統的開發者提供,但嵌入式操作系統的用戶在需要的時候也可以編寫屬于自己的中斷服務任務,這樣的中斷服務任務是應用軟件的一部分。

各種中斷服務程序的具體內容雖然不同,但其結構都基本相似,一般需要做以下工作:

① 保存CPU的上下文,主要是各寄存器的內容,以便在退出中斷服務程序之前進行恢復;

② 如果中斷向量被多個設備所共享,為了確定產生中斷請求信號的設備,需要輪詢這些設備的中斷狀態寄存器;

③ 獲取與中斷相關的其他信息;

④ 對中斷進行具體的處理;

⑤ 恢復CPU的上下文;

⑥ 執行中斷返回指令,使CPU返回到被中斷的程序繼續執行。

盡管發生自陷和異常的原因與中斷不同,但自陷和異常的處理程序的結構都和中斷服務程序基本相同。都大致由保存CPU上下文、進行具體處理、恢復CPU上下文等幾個部分所組成。

上面描述的是對一個中斷進行處理的過程。中斷是異步的過程,經常會出現在一個中斷還未處理完時又發生了另外一個中斷的情況。我們稱這種情況為同時發生了多個中斷,這時有兩種處理方式。第一種是非嵌套的中斷處理方式,第二種是按優先級嵌套的中斷處理方式。

在非嵌套的中斷處理方式下,執行中斷服務程序的時候將屏蔽其他的中斷請求。如圖2.30所示,如果在執行中斷服務程序的時候發生了其他的中斷請求,這個中斷請求將被掛起。當中斷服務程序執行完之后,才重新允許響應中斷,被掛起的中斷請求才會被處理。非嵌套的中斷處理方式沒有考慮中斷的優先級,完全按照中斷請求發生的順序對其進行處理,因此不能保證高優先級的中斷請求及時得到處理。

圖2.30 非嵌套的中斷處理方式

圖2.31 按優先級嵌套的中斷處理方式

按優先級嵌套的中斷處理方式為每類中斷定義一個優先級,并允許高優先級中斷請求中斷低優先級中斷請求的處理過程。如圖2.31所示,在這種中斷處理方式下,由于中斷服務程序只屏蔽那些比當前中斷優先級低或是與當前中斷優先級相同的中斷,所以在完成必要的上下文保存工作后系統立即允許響應高優先級的中斷請求。這時如果有高優先級的中斷請求發生,中斷服務程序在保存當前的工作現場后將轉去執行高優先級中斷的服務程序。當高優先級中斷的服務程序執行完之后,才繼續執行先前被中斷的中斷服務程序。這種處理方式保證了高優先級的中斷請求能及時得到處理。

由于中斷服務程序運行時屏蔽了新的中斷請求,或至少屏蔽了低優先級的中斷請求,所以中斷服務程序的運行時間應盡可能的短,以保證其他中斷請求和任務能夠及時地得到處理。

在中斷處理的工作量是一個恒定值的前提下,縮短中斷服務程序運行時間的方法是將一部分工作交給相關的中斷服務任務去完成,而中斷服務程序只進行一些必須由它來完成的操作。例如,外部設備的中斷服務程序可以只進行一些與外部設備相關的數據讀/寫,并在需要的情況下向外部設備發送確認信息,然后喚醒外部設備的中斷服務任務,而對數據的進一步處理則由中斷服務任務來完成。但喚醒和執行中斷服務任務需要付出一定的代價,在某些情況下并不能提高系統的性能,有可能還得不償失。因此對于一個具體的中斷而言,是否應當引入中斷服務任務需要軟件的設計者根據情況做出判斷。

下面的例2.1說明了中斷服務程序和中斷服務任務之間的配合關系。

例2.1 中斷服務程序和中斷服務任務之間的配合關系

            / ????????????????????????????????????????????????????????????????????/
            /?中斷服務任務                                                                  ?/
            /?功能: 處理中斷服務程序通過消息機制傳遞過來的數據                             ?/
            / ????????????????????????????????????????????????????????????????????/
            DeviceIST()
            {
              while(1)
              {
              /?從中斷服務程序接收消息,如果沒有來自中斷服務程序的消息中斷服務任務被阻塞?/
              wait messge from isr();
              /?處理通過消息從中斷服務程序傳遞過來的數據?/
              process data();
              }
            }
            / ????????????????????????????????????????????????????????????????????/
            /?中斷服務程序                                                                  ?/
            /?功能: 從中斷設備得到數據,并利用消息機制將數據傳遞給中斷服務任務。          ?/
            / ????????????????????????????????????????????????????????????????????/
            DeviceISR()
            {
              ……
              /?從中斷設備得到數據?/
              get data from device();
              /?通過消息機制將數據發送給中斷服務任務?/
              send message to ist();
              ……
            }

在例2.1中,DeviceISR是中斷服務程序,用來從發生中斷的設備獲取數據,但并不對數據進行處理。該中斷服務程序得到數據后,通過消息將數據傳送給中斷服務任務DeviceIST,并將該任務喚醒。處理數據的工作完全由中斷服務任務DeviceIST來完成。

在編寫中斷服務程序時還有一個問題需要注意,這就是不要調用那些可能引起程序阻塞的系統調用。如獲得信號量的調用、分配內存的調用、建立子任務的調用。這是由于中斷服務程序是不受調度程序控制的,并且優先于任務被運行。如果出現中斷服務程序被阻塞的情況,將導致中斷不能被及時處理,其余工作也就無法按時繼續進行,這將嚴重影響整個系統的實時性。

3. 嵌入式操作系統中與中斷相關的幾個技術措施

保證系統的實時性和可靠性是實時嵌入式操作系統的重要設計原則。為達到這些目的,實時嵌入式操作系統經常采取以下一些與中斷相關的技術措施。

(1)臨界區代碼方面的技術措施

中斷延遲是實時嵌入式操作系統的一個重要時間性能指標。因執行臨界區代碼而關中斷是產生中斷延遲的重要原因。因此,在設計實時嵌入式操作系統內核的臨界區代碼時,應遵循以下原則:①臨界區代碼應該盡可能地短小,只把確實必要的代碼放入臨界區中;②在臨界區中盡量控制函數調用的層數,避免函數調用的嵌套層次太深;③盡可能地把那些相對比較耗時的操作放在臨界區之外;④在退出臨界區后,應馬上重新打開中斷,以便系統能檢查是否有因為進入臨界區而沒有處理的中斷,并加以處理。

(2)中斷嵌套方面的技術措施

操作系統采用的中斷處理方式有兩種:一種是非嵌套的中斷處理方式,另一種是按優先級嵌套的中斷處理方式。由于中斷嵌套會使內核的設計變得很復雜,因此一些沒有實時要求的嵌入式操作系統一般不支持中斷嵌套,但是在實時嵌入式操作系統中,為了提高操作系統對于外部事件的實時響應能力一般是采用按優先級嵌套的中斷處理方式。

(3)堆棧方面的技術措施

在支持中斷嵌套的情況下,如果出現多層中斷嵌套,就需要保存很多的上下文信息,中斷服務程序所占用的任務堆??臻g就會很大,并有可能導致任務堆棧溢出。這是一種非常危險的情況。

解決這個問題的方法之一是在給每個任務分配堆棧空間時,都考慮中斷嵌套層數最多的情況,分配足夠大的空間。但這種方法對于內存資源非常有限的嵌入式系統很難行得通。

解決這個問題的另一種方法是在操作系統中采用一個中斷堆棧,專門用來保存中斷嵌套時產生的上下文信息。每當中斷發生時,操作系統立刻切換到使用中斷堆棧,因此中斷發生時需要保存的上下文信息將存儲在中斷堆棧中,而不是存儲在任務堆棧中。通過使用單獨的中斷堆棧,降低了由于對任務堆棧空間的需求的不確定性所帶來的問題,提高了系統的可靠性。

2.2.6 輸入/輸出管理

輸入/輸出管理雖然不是嵌入式操作系統必須提供的一項功能,但除了一些非常精煉的系統之外,許多嵌入式操作系統都支持這一功能。

如圖2.32所示,在嵌入式操作系統中,輸入/輸出管理一般通過I/O模塊和設備驅動程序兩部分來完成。它們共同構成了嵌入式操作系統中的輸入/輸出系統。I/O模塊是硬件無關的軟件。它可以放在操作系統內核中,也可以是內核之外的一個服務任務。I/O模塊里面包括主設備號機制、設備名表機制、文件描述符表機制3個主要部分。設備驅動程序是硬件相關的軟件。不同設備的設備驅動程序在實現上有相當大的差異。為了屏蔽硬件上的這種差異,嵌入式操作系統通常都定義一個統一的設備驅動程序接口,要求設備驅動程序的編寫者按照一定的格式實現以下幾種設備操作函數:

圖2.32 輸入/輸出系統的組成

① 初始化設備(init);

② 打開設備(open);

③ 關閉設備(close);

④ 讀設備(read);

⑤ 寫設備(write);

⑥ 控制設備(control)。

實際上,所謂的設備驅動程序就指的是這幾個函數。各種設備之間的差異都可以被包裝在這些函數之中。例如,從I/O模塊的角度來看,從一個串行端口讀入數據,與從鍵盤讀入數據沒有什么不一樣,都是調用設備驅動程序中的read函數。

1. 主設備號機制

嵌入式操作系統賦予了每一個設備一個主設備號和一個次設備號。主設備號用來選擇設備的驅動程序。主設備號相同的設備均使用同一個設備驅動程序。由于一個驅動程序可能管理多個同類的設備,所以還需要一個次設備號來區別同一類中的不同設備。比如,嵌入式計算機系統上的多個串行端口之間只在某些參數方面有差異(如設備地址),可用同一個驅動程序來處理。這些串行端口共用一個主設備號,但每一個串行端口各自有一個次設備號。

如圖2.33所示,輸入/輸出系統通過驅動程序地址表來管理設備驅動程序。驅動程序地址表中包含各種設備驅動程序的入口地址。主設備號是訪問驅動程序地址表的索引。用主設備號訪問驅動程序地址表,就可以得到init、open、close、read、write、control等函數的入口地址。

圖2.33 驅動程序地址表

2. 設備名表機制

對應用程序的開發者來說,直接使用設備號很不方便,因此一些嵌入式操作系統提供了按名字使用設備的功能。設備名表的作用就是為了實現這種功能。通過設備名表可以將設備名映射為對應的主設備號和次設備號。

如圖2.34所示,設備名表中有設備名、主設備號和次設備號等欄目。每一個表項對應于一個設備。如果有了一個設備的設備名,以這個設備名為關鍵字查找設備名表就可以得到設備的主設備號,用這個主設備號就可以訪問驅動程序地址表,得到驅動程序的入口地址。

圖2.34 設備名表

3. 文件描述符表機制

一些嵌入式操作系統為了方便用戶的使用,支持與使用文件相一致的方法使用系統中的設備。文件描述符表的作用就是實現這種功能。在支持文件描述符表機制的操作系統中,用戶打開一個設備(或文件)后就會在文件描述表中為該設備建立一個表項,并分配一個文件描述符。

如圖2.35所示,一個設備的文件描述符指定了該設備在文件描述符表中的位置。文件描述符表中記錄著當前打開的設備(或文件)的信息,如設備(或文件)的打開方式、設備(或文件)讀寫指針的位置等。其中的“名表索引”欄目的值指向被打開的設備在設備名表中的對應表項,以該值為索引訪問設備名表就可以得到設備的主設備號。以主設備號為索引訪問驅動程序地址表就可以得到驅動程序的入口地址。

圖2.35 文件描述符表

2.2.7 時間管理

時間管理對嵌入式系統有非常重要的作用,是支持系統實現實時響應所必須的功能。幾乎所有的嵌入式操作系統都提供了時間管理的基本機制。

1. 時間管理的硬件基礎

在嵌入式系統上一般有兩種不同的時鐘源:一種是實時時鐘,另一種是定時器/計數器。實時時鐘是一個專門的硬件,靠電池供電,即使系統斷電,也可以保持時間不丟失。由于它獨立于操作系統,所以也稱硬件時鐘,它為嵌入式系統提供一個永久的計時。在嵌入式微處理器上通常都會集成若干定時器/計數器。從硬件角度看,定時器和計數器是兩個可以互換的概念,其差別主要體現在如何使用它們。

如圖2.36所示,構成定時器/計數器的主要部件是一個計數寄存器。這個計數寄存器有一個時鐘信號輸入端和一個脈沖信號輸出端。通過軟件可以為計數寄存器設置一個初始值。隨后到來的每一個時鐘信號都會導致該值增加。當計數寄存器溢出時,會產生一個脈沖信號,這個脈沖信號可以被送到中斷控制器上,引發時鐘中斷。

圖2.36 定時器/計數器

為了使計數寄存器能夠自動重新裝入初始值,需要有一個緩沖寄存器。計數寄存器的初始值被保存在這個緩沖寄存器中。計數寄存器溢出,并產生脈沖信號之后,緩沖寄存器中的數據將自動地被重新裝入到計數寄存器中。這樣,計數寄存器將從其初始值重新開始進行計數。如此循環往復,計數寄存器就將周期性地產生脈沖信號。

嵌入式操作系統可以用微處理器上集成的某個定時器/計數器做時鐘源來產生系統時鐘。系統時鐘的工作過程完全由嵌入式操作系統控制,嵌入式操作系統通過讀取實時時鐘來對系統時鐘進行初始化,系統時鐘的精度也取決于嵌入式操作系統。因此系統時鐘并不是一個永久的時鐘,只有在嵌入式操作系統啟動之后它才有效,并與實時時鐘一起運行。

2. 時間管理的主要功能

嵌入式操作系統所實現的時間管理功能與時鐘中斷有密切的關系。大部分時間管理功能是由時鐘中斷的中斷服務程序實現的。時鐘中斷由計數寄存器所產生的脈沖信號引發。脈沖信號的產生周期叫做“脈沖周期”。在一個“脈沖周期”內會引發一次時鐘中斷。通過調整定時計數寄存器的初始值可以使“脈沖周期”等于不同的時間。例如,一個“脈沖周期”可以為3ms,也可以為6ms。發生時鐘中斷時,時鐘中斷服務程序將被執行。該中斷服務程序可以實現系統時鐘管理、時間片計時管理、任務等待計時管理、軟件定時器管理等功能。

(1)系統時鐘管理

系統時鐘通常按相對時間來計算(如相對于嵌入式操作系統的發布時間。如果嵌入式操作系統發布于2001年1月1日,則系統時鐘是自2001年1月1日0時0分0秒以來所經歷的時間),其單位是“脈沖周期”。每發生一次時鐘中斷,時鐘中斷服務程序就將系統時鐘的值加1,根據“時鐘周期”對應的時間長度,可以把系統時鐘轉換為以秒或毫秒為單位,或轉換為時、分、秒格式的日歷時間。

“脈沖周期”的長短決定了系統時鐘的精度。因為一個“脈沖周期”對應于一次時鐘中斷,精度越高,發生時鐘中斷的頻率也越高。當時鐘中斷的頻率達到一定值時,就會影響系統的正常運行。因此提高系統時鐘的精度是有代價的,并不是精度越高越好,而是應當根據嵌入式系統的實際需求選擇一個合理的值。

(2)時間片計時管理

如果操作系統對任務的運行時間有時間片的限制(時間片以“脈沖周期”為單位),則需要在時鐘中斷服務程序中對當前正在運行的任務的已執行時間進行更新,使任務的已執行時間數加1。執行加1操作后,如果當前任務的已執行時間已經和應執行時間相等,則表示當前任務應當退出運行。這時需要置位調度標志,并把當前任務放到就緒隊列中。中斷服務程序結束運行時會檢測調度標志,如果調度標志被置位,就會運行調度程序,選擇一個新的任務投入運行。

(3)任務等待計時管理

許多嵌入式操作系統都提供一個sleep()系統調用。它使任務睡眠一定的時間(應用程序給出的睡眠時間需要轉換成以“脈沖周期”為單位)。處于睡眠狀態的任務需要延遲運行,其任務控制塊將被放到一個專門的時間阻塞隊列中。發生時鐘中斷后,中斷服務程序需要對時間阻塞隊列中各個任務的剩余延遲時間進行減1操作。剩余延遲時間減1后為0的任務將重新進入就緒狀態,等待被投入運行。

(4)軟件定時器管理

一些嵌入式操作系統能夠支持軟件定時器,應用程序可以通過操作系統提供的系統調用建立和使用軟件定時器。建立軟件定時器時,應用程序要給出一個定時值(應用程序給出的定時值需要轉換成以“脈沖周期”為單位)。發生時鐘中斷后,中斷服務程序將對已經啟動的軟件定時器的定時值進行減1操作。當軟件定時器的定時值減為0時,將觸發建立軟件定時器時注冊的服務函數。應用程序可以在此服務函數中完成某些自己所需要的操作。

3. 時間管理的系統調用

在時間管理方面,嵌入式操作系統提供的系統調用主要有兩類:一類是時鐘管理的系統調用,另一類是軟件定時器管理的系統調用。

時鐘管理的系統調用通常包括以下兩種。

① 設置系統時間:這一系統調用以日歷時間的形式設置系統的當前時間。日歷時間即以年、月、日、時、分、秒形式表示的時間。

② 獲得系統時間:這一系統調用獲得以日歷時間形式表示的當前系統時間。

軟件定時器管理的系統調用通常包括以下5種。

① 建立軟件定時器:為被建立的軟件定時器分配必要的數據結構并注冊服務程序,但并沒有開始計時。

② 啟動軟件定時器:使其開始計時。計時時間到后,將觸發在建立軟件定時器時所注冊的服務程序。

③ 停止軟件定時器計時:使軟件定時器停止計時。停止計時后,軟件定時器的服務程序將不再被觸發,除非軟件定時器被重新啟動。

④ 復位軟件定時器:使軟件定時器的定時值恢復到建立時所設定的值。

⑤ 刪除軟件定時器:把軟件定時器所占用的資源還給系統。刪除軟件定時器時,尚在計時的軟件定時器將自動停止工作。

在許多嵌入式操作系統中,時間管理并不完全由內核來實現。對于這樣的系統,上述的某些系統調用就演變為與系統內核沒有直接關系的庫函數。

2.2.8 電源管理

電源管理雖然不是嵌入式操作系統必須提供的一種功能,但由于很多嵌入式系統都是通過電池供電,所以盡可能地降低功耗對嵌入式系統來說有重要的意義。因此有相當一部分嵌入式操作系統都支持電源管理功能。

1. 電源管理的硬件基礎

在嵌入式操作系統上實現以降低功耗為目的電源管理需要嵌入式系統的硬件給予必要的支持。實際上,大多數嵌入式系統的CPU和外部設備都考慮了如何降低功耗的問題,可以提供多種電源管理的機制。

就CPU來說,一般可以提供以下4種機制。

① CPU可處于關閉狀態。此時將與外部總線斷開(外部總線處于三態狀態)。

② CPU中的某些單元可以單獨關閉,如高速緩存。

③ CPU能夠工作于不同的時鐘頻率。時鐘頻率越高,其功耗越大,反之功耗越低。

④ CPU能夠工作于多種電壓。CPU運行時電壓越高,其功耗越大,反之功耗越低。

就外部設備來說,一般可以提供以下3種機制。

① 關閉設備中的某些部件,只維持設備的基本功能。

② 使設備處于掉電狀態,但內部時鐘仍保持運行。此時設備不能提供正常的功能,但可以很快回到正常工作狀態。

③ 使設備處于掉電狀態,并且內部時鐘也停止運行。此時設備不能提供正常的功能,重新回到正常工作的狀態也需要較長的時間。

CPU和外部設備所提供的電源管理機制可以由軟件控制。這些機制是降低嵌入式系統功耗的基礎,但是要在保證系統正常運行的前提下實現合理降低功耗的目的,還需要嵌入式操作系統和應用軟件的配合。操作系統可以在硬件提供的基本機制上實現一系列統籌考慮整個系統電源管理的功能,應用軟件則可以根據自身的具體情況采取一些有針對性的降低功耗的措施。

2. 電源管理的主要功能

在嵌入式操作系統中電源管理工作由電源管理模塊來承擔。電源管理模塊位于操作系統的內核之中,或作為一個任務運行于內核之外,其作用是在硬件所提供的電源管理功能的基礎上利用軟件進一步提高嵌入式系統的節電效果。嵌入式操作系統的電源管理模塊主要實現以下功能。

① 在硬件支持的電源管理機制的基礎上,實現若干功耗不同的用電模式。

② 根據用戶的配置,完成電源管理的初始化工作,使嵌入式系統處于一種正常的用電模式下。

③ 監控系統的運行狀況,收集系統中任務和外部設備的狀態信息。

④ 定義并實現一系列用電模式轉換規則,根據用電模式轉換規則或應用程序的請求實現用電模式的轉換。用電模式的轉換與系統中任務和外部設備的狀態有關。

⑤ 提供若干供應用程序調用的系統調用,以便應用程序可以根據具體情況實現合乎自身特點的節電措施。這些系統調用可起到將硬件所提供的電源管理機制與應用程序隔離的作用,使應用程序對硬件的用電特性的控制通過某種“標準”接口實現,因而硬件的變化不會影響到應用級的電源管理策略。

目前常用的電源管理技術規范有APM(Advanced Power Management)和ACPI(Advanced Configuration and Power Interface)。這是兩個針對PC特別是筆記本電腦的技術規范。其制定者是一些計算機硬件和軟件制造商。兩者之中,APM出現的時間較早,ACPI出現的時間較晚。相對于PC,嵌入式計算機雖然有一定特殊性,但也可以參考這兩個技術規范實現電源管理功能。

APM是一個適用于BIOS的技術規范。它定義了5種用電模式,分別是常規模式、待命模式、掛起模式、睡眠模式和關閉模式。其中,常規模式的功耗最高,待命模式次之,掛起模式再次之,睡眠模式更次之,而關閉模式的功耗為0。

①常規模式是正常的工作模式。在這一模式下,CPU的所有單元(包括運算器、各種寄存器、時鐘、高速緩存、系統總線等)和所有的外部設備都處于上電狀態。系統的功耗最大,性能也最好。

②待命模式下,CPU處于關閉狀態,但內存中數據仍被保留,并且只有小部分外部設備被關閉,系統可以很快地回到正常工作的狀態。

③ 掛起模式與待命模式比較近似,但大部分外部設備被關閉,因此系統回到正常工作的狀態需要較長的時間。

④在睡眠模式下,CPU處于關閉狀態,系統停止給內存供電,內存數據被寫入磁盤中,外部設備也停止工作,只有系統時鐘處于活動狀態。

⑤ 在關閉模式下,CPU和所有的外部設備都被關閉,系統的功耗為0。

從內部實現的角度看,常規模式、待命模式、掛起模式、睡眠模式和關閉模式之間的關系如圖2.37所示。

圖2.37 幾種用電模式之間的轉換關系

系統上電之后處于常規模式。如果一直有任務在運行或是有中斷發生(意味著需要運行中斷服務程序),則系統保持該模式;否則,從該模式切換到待命模式。

在待命模式下,需要啟動一個定時器,記錄系統持續處于待命模式的時間。如果在定時器計時到時之前有中斷發生,則系統將回到常規模式,并取消定時器計時;如果在定時器計時到時之前一直沒有中斷發生,則系統將在定時器計時滿后切換到掛起模式。這是因為系統長時間沒有工作可做就應進一步降低功耗。

在掛起模式下,也需要啟動一個定時器,記錄系統持續處于掛起模式的時間。如果在定時器計時到時之前有中斷發生,則系統將回到常規模式,并取消定時器計時;如果在定時器計時到時之前一直沒有中斷發生,則系統將在定時器計時到時后進入睡眠模式,以便更進一步地降低功耗。

在睡眠模式下出現外部中斷時,系統將回到常規模式;如果沒有中斷發生系統就一直處在該模式下,致使功耗相當的低。

ACPI彌補了APM的一些缺陷,并增加了一些新的功能和特性,如軟件關機。其適用范圍也不再僅局限于BIOS,而是擴大到了操作系統一級。ACPI共定義了6種用電模式,分別是S0、S1、S2、S3、S4、S5。

① S0是正常工作模式。此時,CPU和所有的外部設備都處于上電狀態。系統的功耗最大,性能也最好。

② 在S1模式下,CPU被關閉,但外部設備仍然處于正常工作狀態。

③ 在S2模式下,CPU被關閉,總線時鐘也被關閉,但外部設備仍然處于正常工作狀態。

④ 在S3模式下,除了將CPU關閉之外,還停止給內存供電。

⑤在S4模式下,CPU和系統中的大部分外部設備都被關閉,但硬盤依然帶電,并隨時可以進入正常工作狀態。

⑥ 在S5模式下,CPU和所有的外部設備都被關閉,系統的功耗為0。

3. 電源管理的系統調用

應用程序與硬件、操作系統緊密配合對降低系統功耗有極大的作用。操作系統很難檢測到某些顯著降低系統功耗的機會,而應用程序卻可以很容易地發現這些機會。因此在應用程序中,根據自身的具體情況采取一些有針對性的措施可以更有效地降低系統的功耗。下面的幾個例子都說明了這個問題。

例如,一些應用軟件(如音頻播放軟件)在較低時鐘頻率下運行與在較高時鐘頻率下運行效果基本相同,因而在運行這些軟件時可以采用較低的時鐘頻率。一些應用軟件在運行時根本不需要使用CPU的某些單元,或系統中的某些外部設備,因此在這些軟件運行時可以將不使用的CPU單元或外部設備關閉。一些應用軟件在啟動一個外部設備后,要等待來自該設備的中斷,在等待中斷發生的時間區間內關閉CPU的大部分模塊、甚至整個CPU,對軟件的功能不會產生影響。

為了使應用程序可以根據本身的情況進行電源管理,嵌入式操作系統必須給予支持,提供一些有關電源管理的系統調用。各種嵌入式操作系統提供的電源管理系統調用雖然有比較大的差別,但通常會包括以下幾方面的功能:

① 設置系統的用電模式及相關的信息;

② 查詢系統的用電模式及相關的信息;

③ 設置CPU和外部設備的用電方式,如關閉CPU中的某些單元,通過這類系統調用,應用程序可以直接對CPU和外部設備的功耗進行控制;

④ 查詢CPU和外部設備的用電方式以及與電源管理相關的其他信息,如電池的容量。

在許多嵌入式操作系統中,電源管理并不由內核來實現。對于這樣的系統,提供給應用程序的電源管理功能在形式上就不是系統調用,而是一些庫函數。

2.2.9 看門狗

支持看門狗(Watch-dog)的嵌入式操作系統雖然不普遍,但對于專門用于實時控制的嵌入式操作系統來說,看門狗卻是一種很重要的功能。

嵌入式計算機在運行過程中由于自身因素和外界的影響有可能會陷入“死循環”或“死鎖”狀態。比如在受到強電磁場干擾時就會發生這種情況。系統進入“死循環”或“死鎖”對用于控制的嵌入式系統來說是一個非常嚴重的問題。因為這類嵌入式系統經常是在無人照看,甚至管理人員難以到達的地方運行。萬一系統中的某段程序陷入了死循環或者死鎖,使整個系統“死掉”的時候是沒有人能夠及時知道并處理的,這將導致控制對象長時間失控,甚至發生嚴重后果。

即使嵌入式計算機所在位置的管理人員能夠比較方便地到達,在系統陷入“死循環”或“死鎖”狀態的時候,處理起來也不像通用計算機那樣簡單。在通用計算機中,個別任務陷入死循環對于整個系統的影響并不致命性。以Linux為例,陷入死循環的任務在用完了它的時間片以后就會退出運行,通過kill命令就可以將其“殺死”。可是在實時嵌入式操作系統中則大不一樣。實時嵌入式操作系統是按優先級高低進行調度的,如果陷入死循環的任務優先級很高,那么比它優先級低的所有任務就永遠不會有機會運行,因而也就充當不了“殺手”。

因為上述原因在專門用于控制的嵌入式計算機中很需要一種能監控系統運行狀況,并在系統進入“死循環”或“死鎖”狀態時可以重新啟動系統,使其回到正常狀態的功能??撮T狗就正是這樣一種功能。

看門狗功能需要在硬件和軟件的配合下實現。其硬件的主體是一個對CPU時鐘脈沖進行計數的計數器。一些專門用于嵌入式系統的處理器上都帶有支持看門狗的電路,此外還有一些專門支持看門狗的芯片,利用這種芯片就可以幫助本身不帶看門狗電路的處理器實現看門狗功能。

實現看門狗功能的方案有很多種。其中之一是讓硬件計數器在計數到某個預定值時就引發一個不可屏蔽中斷,而這個不可屏蔽中斷的服務程序則讓系統重新啟動。同時,在操作系統中安排一系列控制點,使得每當CPU執行到這些控制點時,就把計數器清0,不讓它達到能引發不可屏蔽中斷的值。這樣,只要系統還在正常運行,系統就不會被重新啟動??墒牵绻心硞€高優先級的任務陷入了死循環,從而使CPU不能執行到這些控制點的時候,計數器就會一直計數到預定的數值,從而引發不可屏蔽中斷,使系統重新啟動。

主站蜘蛛池模板: 寿宁县| 大邑县| 宽甸| 河池市| 汾西县| 福建省| 常德市| 班戈县| 长宁区| 肥西县| 商都县| 沙湾县| 盈江县| 新龙县| 阳谷县| 余江县| 恩施市| 五常市| 南木林县| 肃南| 高雄县| 云南省| 文水县| 台江县| 旌德县| 济源市| 中阳县| 汝阳县| 彭阳县| 邵武市| 崇州市| 丹东市| 内黄县| 波密县| 南澳县| 苍梧县| 甘洛县| 措勤县| 个旧市| 金川县| 阳江市|