由通信提出的問題
在裸機編程的過程中,我們經常會遇到函數需要另一些函數的數據信息,也就是通信,這時我們會怎么做呢?進行裸機開發的同學肯定都會說:使用全局變量,通過指針實現之類。使用全局變量快捷且高效。
但是在RTOS系統中,這會遇到一些問題:怎樣防止許多線程同時進行對這個變量的訪問?怎樣觀測通信是否已經發生,從而進行通信之后的工作?(例如:進行優先級轉換,或者進行數據的處理)如果有個線程比較磨蹭,通信遲遲不發生,CPU豈不是要尷尬地等待好久。。
對於這些問題,操作系統給出了自己的通信方法與函數供使用者調用,當然,也最好使用操作系統的通信機制。
我們打開RTT 的IDE,然后打開內核的Setting,會看到RTT的一些設定:
可以看到,線程間通信一欄,這就是RTT給開發者提供的通信機制,在實際開發中,這些機制可以滿足絕大部分需求。RTT系統默認打開了信號量,互斥量,事件集,郵箱,消息隊列五種機制。
信號量:
二值信號量:
在我們的裸機開發過程中,經常會使用一個變量:Flag,大多數時候,這個叫做Flag的變量會在0和1之間反復橫跳,用於標記某一件事情是否發生,並經常被用於中斷。這個Flag就是一個信號量,實際上,它是我們最常使用的通信方式之一。
在RTT系統中,信號量會被獲取或者釋放(由獲取/釋放函數實現),當它被獲取時,就為0,當它被釋放,就為1。這樣的信號量被稱為二值信號量。
舉一個例子(偽代碼):
static void xxx1() { while(1) { /*一個傳感器采集一個特別復雜的數據,需要3S,不需要CPU參與*/ xxx } } static void xxx2() { while(1) { /*將這個數據交給CPU進行分析,數據無效就舍棄*/ xxx
rt_thread_mdelay(xx) } }
在這個例子中,有兩個線程,線程2中,CPU一直在等待數據,但數據卻總是無效的(因為傳感器還沒采集到數據),這讓CPU一直在進行數據驗證,雖然CPU中間進行了一些延時,但仍然在無效數據上浪費了時間,這時,就需要信號量出馬了
改良后數據如下(偽代碼):
rt_sem_create(xxx); //信號量創建函數,參數中默認為1,表示是釋放的,具體參數不贅述 static void xxx1() { while(1) { rt_sem_take(xxx) //獲取信號量,這時信號量被置0 /*一個傳感器采集一個特別復雜的數據,需要3S,不需要CPU參與*/ xxx rt_sem_release(xxx) //釋放信號量,這時信號量被置1 } } static void xxx2() { while(1) { rt_sem_take(xxx) //獲取信號量,當信號量已經被其他線程獲取,就進入阻塞態等待,等待時間自定,這里不贅述 /*將這個數據交給CPU進行分析,數據無效就舍棄*/ xxx rt_sem_release(xxx) //釋放信號量 rt_thread_mdelay(xx) } }
這里我們看到,當傳感器開始工作時,信號量已經被獲取了,這時進入線程二想要處理數據,就會進入阻塞態,讓出CPU去做其他的事情,當傳感器獲得數據后,就會釋放信號量,這時,線程二就從阻塞態中退出,從而去處理有效的數據。(注意:獲取信號量進行相關操作后應立刻釋放信號量,如果不進行釋放,可能會導致其他線程無法運行)
經過這一番操作,無效數據不會被一次又一次的處理了,而是在傳感器獲得數據后,CPU直接去處理有效的數據,節省了許多無效操作。
計數型信號量:
計數型信號量與二值信號量類似,只不過范圍從二值的[0,1],變為了[0,65535],這樣它就可以被多個線程獲取,獲取的最大數目由使用者自定,使用的系統函數與二值信號量相同。
計數信號量經常被用於控制一些公共資源的訪問數量,例如一個資源最多可以被x個線程同時訪問,多於x時效率大大下降或者會出現錯誤(有點像網站服務器限制訪客數量)。
互斥量:
互斥量是一種特殊的二值信號量,其與二值信號量的最大區別為引入了優先級繼承機制。
優先級繼承機制:如果一個高優先級的線程去申請一個已經被低優先級獲得的互斥量,高優先級會進入到阻塞態,而持有這個互斥量的線程地位會瞬間與高優先級一樣,取得高優先級相同的優先等級,這就是“優先級繼承”
這樣有什么好處呢?這里依然是一個栗子。
static void HIGH() { xxx } static void MIDDLE() { xxx } static void LOW() { xxx }
假設有三個線程,優先度分別為高,中,低。理所當然,開發者希望高優先級的越快處理越好,低優先級的則不是很緊要。
這時,如果有個二值信號量,被低優先級(LOW)獲取。然后高優先級(HIGH)的也想要獲取這個信號量,於是進入了阻塞等待。
但是當LOW准備用CPU運行時,MIDDLE卻發話了:我有更緊要的任務,你讓開。LOW小弟就被擠到了一邊,畢竟它是優先級最低的。
這就產生了一個問題,開發者最希望運行的HIGH在等待信號量,而MIDDLE卻在運行(假設MIDDLE和HIGH無關聯,此時HIGH就要等待MIDDLE運行+LOW運行,而不是只等待LOW運行),LOW在等待CPU讓出。這顯然違背了開發者“高優先級越快越好”的想法。
怎樣解決這個問題,這時就引入互斥量來解決這個問題,當HIGH進入阻塞態之后,LOW瞬間“繼承”了HIGH的優先級,LOW小弟也暫時體驗了當大哥的感覺,並把MIDDLE擠到一邊,快速的處理起了數據,並在處理完后變回原來的優先級,將互斥量釋放,HIGH得以繼續運行。這樣,通過暫時提高LOW的優先級,開發者所希望的HIGH處理越快越好的想法得以實現。
(暫時記錄信號量和互斥量的學習筆記)