0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

AWorks軟件設計,郵箱、消息隊列和自旋鎖使用方法

AGk5_ZLG_zhiyua ? 來源:互聯(lián)網(wǎng) ? 作者:佚名 ? 2018-06-13 09:13 ? 次閱讀

本文導讀

本文介紹了郵箱、消息隊列和自旋鎖的使用方法。信號量只能用于任務間的同步,不能傳遞更多的信息,為此,AWorks提供了郵箱和消息隊列服務,它們的主要區(qū)別在于支持的消息長度不同,在郵箱中,每條消息的長度固定為4字節(jié),而在消息隊列中,消息的長度可以自定義。本文為《面向AWorks框架和接口編程(上)》第三部分軟件篇——第10章——第3~5小節(jié):郵箱、消息隊列和自旋鎖。

10.3 郵箱

前面介紹了用于任務間同步的三種信號量,它們相當于資源的鑰匙,獲取到鑰匙的任務可以訪問相關(guān)的資源,任務以此確定可以運行的時刻。但是,信號量不能夠提供更多的信息內(nèi)容。比如,按鍵按下時,釋放一個信號量,任務獲取到該信號量時,只能知道有按鍵按下了,但不能知道按鍵相關(guān)的更多信息,比如:具體是哪個按鍵按下了?

當需要在任務間傳遞的更多信息時,可以使用AWorks提供的郵箱服務。郵箱服務是實時內(nèi)核中一種典型的任務間通信方法,特點是開銷比較低,效率較高。一個郵箱中可以存儲多封郵件,郵箱中的每一封郵件只能容納固定的4字節(jié)內(nèi)容(針對32位處理系統(tǒng),指針的大小即為4個字節(jié),所以一封郵件恰好能夠容納一個指針)。發(fā)送郵件的任務(或中斷服務程序)負責將郵件存入郵箱,接收郵件的任務負責從郵箱中提取郵件。示意圖詳見圖10.4。

圖10.4 郵箱工作示意圖

當郵箱中存在多封郵件時,默認按照先進先出(FIFO)的原則傳遞給接收郵件的任務。郵件的大小固定為4字節(jié),當需要傳遞的消息內(nèi)容大于4字節(jié)時,則可以僅將消息的地址作為郵件內(nèi)容,任務接收到郵件時,通過該地址即可查找到相應的消息。這種方式使得使用郵箱進行消息傳遞的效率非常高。

AWorks提供了使用郵箱的幾個宏,宏的原型詳見表10.7。

表10.7 郵箱相關(guān)的宏(aw_mailbox.h)

1. 定義郵箱實體

AW_MAILBOX_DECL()和

AW_MAILBOX_DECL_STATIC()宏均用于定義一個郵箱實體,為郵箱分配必要的內(nèi)存空間,包括用于存儲郵件的空間。它們的原型為:

其中,參數(shù)mailbox為郵箱實體的標識名。mail_num表示郵箱的容量,即郵箱中存儲郵件的最大條數(shù),由于每封郵件的大小為4字節(jié),因此用于存儲郵件的總內(nèi)存大小為:mail_num×4。

兩個宏的區(qū)別在于:

AW_MAILBOX_DECL_STATIC() 在定義郵箱所需內(nèi)存時, 使用了關(guān)鍵字static ,如此一來, 便可以將郵箱實體的作用域限制在模塊內(nèi)(文件內(nèi)), 從而避免模塊之間的郵箱命名沖突,同時,還可以在函數(shù)內(nèi)使用本宏定義郵箱實體。

如使用AW_MAILBOX_DECL()定義一個標識名為mailbox_test的郵箱實體,郵件的最大數(shù)目為10,其范例程序詳見程序清單10.49。

程序清單10.49 定義郵箱實體的范例程序

使用AW_MAILBOX_DECL()定義郵箱實體時,可以將郵箱的實體嵌入到另一個數(shù)據(jù)結(jié)構(gòu)中,其范例程序程序清單10.50。

程序清單10.50 將郵箱實體嵌入到結(jié)構(gòu)體中

也可以使用AW_MAILBOX_DECL_STATIC()定義一個標識名為mailbox_test的郵箱實體,其范例程序詳見程序清單10.51。

程序清單10.51 定義郵箱實體(靜態(tài))的范例程序

2. 初始化郵箱

定義郵箱實體后,必須使用AW_MAILBOX_INIT()初始化后才能使用。其原型為:

其中,mailbox為由AW_MAILBOX_DECL() 或 AW_MAILBOX_DECL_STATIC()定義的郵箱。mail_num表示郵箱可以存儲的郵件條數(shù),其值必須與定義郵箱實體時的mail_num相同。options為郵箱的選項,其決定了阻塞于此郵箱(等待消息中)的任務的排隊方式,可以按照任務優(yōu)先級或先進先出的順序排隊,它們對應的宏詳見表10.8。

表10.8 郵箱初始化選項宏(aw_mailbox.h)

注意,前面講述了三種信號量,在初始化時同樣可以通過選項指定阻塞于信號量的任務的排隊方式,分為按照優(yōu)先級和先進先出兩種方式,它們對應的宏名分別為AW_SEM_Q_PRIORITY和AW_SEM_Q_FIFO。與郵箱選項的命名不同,不可混用。

通常排隊方式都選擇按照優(yōu)先級排隊,初始化郵箱的范例程序詳見程序清單10.52。

程序清單10.52 初始化郵箱的范例程序

AW_MAILBOX_INIT()用于初始化一個郵箱實體,初始化完畢后,將返回該消郵箱的ID,其類型為aw_mailbox_id_t。定義一個該類型的變量保存返回的ID如下:

aw_mailbox_id_t的具體定義用戶無需關(guān)心,該ID可作為后文介紹的其它郵箱相關(guān)接口函數(shù)的參數(shù),用于指定要操作的郵箱。特別地,若返回ID的值為NULL,表明初始化失敗。一般地,若無特殊需求,不會使用該ID,可以不用保存該ID。

3. 從郵箱中獲取一條信息

從郵箱中獲取一條消息的宏原型為:

其中,mailbox為由AW_MAILBOX_DECL() 或 AW_MAILBOX_DECL_STATIC()定義的郵箱。p_data指向用于保存消息的緩沖區(qū),消息獲取成功后,將存儲到p_data指向的緩沖區(qū)中,由于消息的大小固定為4字節(jié)(32位),因此緩沖區(qū)的大小也必須為4字節(jié),例如,可以是一個指向32位數(shù)據(jù)的指針。timeout指定了超時時間。該宏的返回值為aw_err_t類型的標準錯誤號。注意,由于中斷服務程序不能被阻塞,因此,該函數(shù)禁止在中斷中調(diào)用。

如果郵箱不為空,包含有效的消息,則本次操作將成功獲取到一條消息,同時,會從郵箱中將該條消息刪除,有效消息的條數(shù)減1。此時,AW_MAILBOX_RECV()的返回值為:AW_OK。

如果郵箱為空,沒有任何有效的消息,則不能立即成功獲取到消息,接下來具體的行為將由超時時間timeout的值決定。

(1)若timeout的值為

AW_MAILBOX_WAIT_FOREVER。則任務會阻塞于此,一直等待,直到郵箱中有可用的消息,即其它任務或中斷發(fā)送了消息。范例程序詳見程序清單10.53。

程序清單10.53 永久阻塞等待郵箱的范例程序

(2)若timeout的值為

AW_MAILBOX_NO_WAIT。則任務不會被阻塞,立即返回,但不會成功獲取到消息,此時,AW_MAILBOX_RECV()的返回值為:

-AW_EAGAIN(表示當前資源無效,需要重試)。范例程序詳見程序清單10.54。

程序清單10.54 不阻塞等待郵箱的范例程序

(3)若tiemout的值為一個正整數(shù),則表示最長的等待時間(單位為系統(tǒng)節(jié)拍),任務會阻塞于此,在timeout規(guī)定的時間內(nèi),若成功獲取到一條消息,則AW_MAILBOX_RECV()的返回值為:AW_OK;若在timeout規(guī)定的時間內(nèi),沒有獲取到消息,則返回值為-AW_ETIME(表明超時)。范例程序詳見程序清單10.55。

程序清單10.55 等待郵箱的超時時間為500ms的范例程序

4. 發(fā)送一條消息到郵箱中

發(fā)送消息到郵箱中的宏原型為:

其中,mailbox為由AW_MAILBOX_DECL() 或 AW_MAILBOX_DECL_STATIC()定義的郵箱。data為發(fā)送的32位數(shù)據(jù)。timeout指定了超時時間。priority指定了消息的優(yōu)先級。該宏的返回值為aw_err_t類型的標準錯誤號。

若郵箱未滿,可以繼續(xù)存儲消息,則該消息發(fā)送成功,郵箱中的有效消息條數(shù)加1。此時,AW_MAILBOX_SEND()的返回值為:AW_OK。

若郵箱已滿,暫時不能繼續(xù)存儲消息,則不能立即成功發(fā)送消息,接下來具體的行為將由超時時間timeout的值決定。

(1)若timeout的值為

AW_MAILBOX_WAIT_FOREVER。則任務會阻塞于此,一直等待,直到消息成功放入郵箱中。即其它任務從郵箱中獲取了消息,郵箱中留出了空閑空間。范例程序詳見程序清單10.56。

程序清單10.56 永久阻塞等待郵箱的范例程序

注意,priority參數(shù)指定了消息的優(yōu)先級,其可能的取值有兩個:AW_MAILBOX_PRI_NORMAL和AW_MAILBOX_PRI_URGENT,分別表示普通優(yōu)先級和緊急優(yōu)先級。一般地,均使用普通優(yōu)先級,此時,新的消息按照先進先出的原則,依次排隊存入郵箱,該消息將后于當前郵箱中其它消息被取出。若使用緊急優(yōu)先級,則新的消息插隊放在郵箱的最前面,該消息將先于當前郵箱中其它消息被取出,即在下次從郵箱中獲取消息時被取出。

(2)若timeout的值為

AW_MAILBOX_NO_WAIT。則任務不會被阻塞,立即返回,但不會成功發(fā)送消息,此時,AW_MAILBOX_SEND()的返回值為:-AW_EAGAIN(表示當前資源無效,需要重試)。范例程序詳見程序清單10.57。

程序清單10.57 不阻塞等待郵箱的范例程序

(3)若tiemout的值為一個正整數(shù),則表示最長的等待時間(單位為系統(tǒng)節(jié)拍),任務會阻塞于此,在timeout規(guī)定的時間內(nèi),成功發(fā)送了消息,則AW_MAILBOX_SEND()的返回值為:AW_OK;若在timeout規(guī)定的時間內(nèi),沒有成功發(fā)送消息,則返回值為-AW_ETIME(表明超時)。范例程序詳見程序清單10.58。

程序清單10.58 等待郵箱的超時時間為500ms的范例程序

注意,在中斷服務程序中(如按鍵回調(diào)函數(shù)),可以使用該接口發(fā)送消息至郵箱,這是中斷和任務之間很重要的一種通信方式:即在中斷中發(fā)送消息,在任務中接收消息并處理,從而減小中斷服務程序的時間。但是,由于中斷不能被阻塞,因此,當在中斷中發(fā)送消息時,timeout標志只能為AW_MAILBOX_NO_WAIT。在這種應用中,為了避免消息丟失,應該盡可能避免郵箱被填滿,可以通過增加郵箱的容量以及提高處理消息任務的優(yōu)先級,使郵箱中的消息被盡快處理。

5. 終止郵箱

當一個郵箱不再使用時,可以終止該郵箱,宏原型為:

其中,mailbox為由AW_MAILBOX_DECL() 或 AW_MAILBOX_DECL_STATIC()定義的郵箱。當郵箱被終止后,若當前系統(tǒng)中還存在等待該郵箱的任務,則任何等待此郵箱的任務將會解阻塞,并返回-AW_ENXIO(表明資源已不存在),其范例詳見程序清單10.59。

程序清單10.59 終止郵箱的范例程序

在講述計數(shù)信號量時,使用了單個按鍵控制LED翻轉(zhuǎn),由于只使用到了一個按鍵,因此,在發(fā)送按鍵消息時,只需要使用計數(shù)信號量對按鍵事件進行計數(shù),無需發(fā)送更多的消息。若需要使用多個按鍵控制LED,則在發(fā)送按鍵消息時,必須攜帶按鍵編碼信息,以便針對不同的按鍵作不同的處理。

例如,要實現(xiàn)一個簡單的應用,通過LED顯示當前按鍵的編碼:

  • KEY_0按下,則LED0熄滅,LED1熄滅,顯示“00”;

  • KEY_1按下,則LED0熄滅,LED1點亮,顯示“01”;

  • KEY_2按下,則LED0點亮,LED1熄滅,顯示“02”;

  • KEY_3按下,則LED0點亮,LED1點亮,顯示“03”。

顯然,為了區(qū)分不同按鍵,發(fā)送按鍵消息時,需要攜帶按鍵的編碼信息,由于按鍵編碼是int類型的數(shù)據(jù),在32位系統(tǒng)中,其恰好為32位,因此,可以使用郵箱來管理按鍵消息。范例程序詳見程序清單10.60。

程序清單10.60 郵箱使用范例程序

在按鍵事件回調(diào)函數(shù)中發(fā)送消息,由于只需要處理按鍵按下事件,因此,僅當按鍵按下時(key_state不為0),才向郵箱中發(fā)送消息(按鍵編碼)。在task_led任務中接收消息,當成功獲取到一條消息時,根據(jù)消息內(nèi)容(按鍵編碼)控制LED。

實際上,這里的按鍵處理程序僅僅用于控制LED燈,耗時時間是非常短的,往往比發(fā)送一條消息的時間還短,這里使用郵箱并不能優(yōu)化程序,僅僅只是作為一個使用郵箱的范例,實際應用按鍵的處理通常會復雜得多,則建議使用這種通用的模式,即在按鍵事件的回調(diào)函數(shù)中,僅僅只是將按鍵編碼發(fā)送到郵箱中,實際的處理在任務中完成。這樣可以避免長時間占用中斷,影響系統(tǒng)的實時性,使其它緊急事務得不到處理,同時,郵箱還有一個緩沖的作用,當按鍵來不及處理時,可以暫存到郵箱中,后續(xù)空閑時再及時去處理,很大程度上避免了“丟鍵”的可能性,就像PC一樣,有時候系統(tǒng)卡頓,顯示屏卡住,但按鍵輸入的信息后續(xù)還是會顯示出來,一般不會丟失。

在上面的范例程序中,由于按鍵編碼的大小為4字節(jié),郵件恰好可以容納,因此,可以直接將按鍵編碼作為消息內(nèi)容,發(fā)送到郵箱中(拷貝一份,存儲至郵箱中)。若要發(fā)送的消息大于4字節(jié),顯然不能直接發(fā)送了,此時,可以將傳輸內(nèi)容的地址作為郵件內(nèi)容,發(fā)送到郵箱中,接收者接收到郵件后,再將郵件內(nèi)容作為地址,從中取出實際的消息內(nèi)容。范例程序詳見程序清單10.61。

程序清單10.61 郵箱使用范例程序——消息內(nèi)容大于4字節(jié)

程序中,定義了兩個任務:task0和task1。task0負責發(fā)送消息,每隔1s將count值加1,若count為奇數(shù),則發(fā)送__g_str1字符數(shù)組中的信息;若count為偶數(shù),則發(fā)送__g_str2字符數(shù)組中的信息。__g_str1和__g_str2兩個字符數(shù)組分別存放了字符串:

"The count is an oddnumber!"和

"The count is an even number!"。顯然,字符串的長度超過了4個字節(jié),因此,兩個數(shù)組的大小也都超過了4字節(jié)。在tsak1發(fā)送消息時,將字符串數(shù)組的地址作為消息發(fā)送到了郵箱中。task1用于接收消息,當接收到消息時,將其作為字符數(shù)組的地址,使用aw_kprintf()將接收到的字符信息打印出來。以此完成了消息的傳遞。

由于郵箱僅傳輸了兩個字符數(shù)組的地址,為了保證接收任務正確提取地址中的實際消息,必須確保接收任務接收到郵件時,地址中的數(shù)據(jù)仍然有效。因此,在范例程序中,將兩個數(shù)組定義為了全局變量,使其內(nèi)存一直有效。甚至在消息處理完成后,數(shù)組的內(nèi)存空間還是有效的。

在一些應用中,當消息處理完畢后,消息將沒有任何實際意義,其地址中對應的數(shù)據(jù)可以丟棄,以釋放相關(guān)內(nèi)存。此時,可以使用動態(tài)內(nèi)存來管理消息:發(fā)送者動態(tài)獲取一段內(nèi)存空間,填充相關(guān)內(nèi)容后,將這段內(nèi)存空間的首地址發(fā)送到郵箱中,接收者從郵箱中獲取到該地址,然后從地址中提取出實際的消息內(nèi)容進行處理,處理完畢后,釋放內(nèi)存。

例如,在程序清單10.60的基礎(chǔ)上,對功能進行簡單的修改:當按鍵按下時,LED顯示當前按鍵的編碼,當按鍵釋放時,熄滅所有LED。顯然,由于需要對按鍵按下和釋放作不同的處理,這就要求在按鍵事件產(chǎn)生后,除需要發(fā)送按鍵編碼信息外,還要發(fā)送按鍵的狀態(tài)(按下或釋放)。此時,消息就需要包含按鍵編碼和按鍵狀態(tài),共計8字節(jié)。范例程序詳見程序清單10.62。

程序清單10.62 郵箱使用范例程序——消息內(nèi)存動態(tài)分配

程序中,使用了aw_mem_alloc()和

aw_mem_free()進行消息內(nèi)存的申請和釋放。

aw_mem_alloc()和aw_mem_free()與標準C的malloc()和free()功能相同,用于動態(tài)內(nèi)存的管理。它們在aw_mem.h文件中聲明。

aw_mem_alloc()的函數(shù)原型為:

其用于分配size字節(jié)的內(nèi)存空間,返回void*類型的指針,該指針即指向分配空間的首地址,若內(nèi)存分配失敗,則返回值為NULL。

aw_mem_free()的函數(shù)原型為:

其用于釋放由aw_mem_alloc()分配的空間,ptr參數(shù)即為內(nèi)存空間的首地址,其值必須是由aw_mem_alloc()函數(shù)返回的。

10.4 消息隊列

前面介紹了郵箱服務,郵箱固定了消息的大小為4字節(jié),當需要傳輸多余4字節(jié)的內(nèi)容時,往往需要使用指針的形式,即使用郵箱傳遞實際消息的首地址,任務間通過傳遞的地址共享信息。使用地址共享信息的效率很高。但是,這種情況下,就需要特別小心的進行內(nèi)存的申請和釋放,一個地址中的消息使用完畢后,需要釋放相關(guān)內(nèi)存空間。對于初學者來講,使用起來相對繁瑣,容易出錯。

為此,AWorks提供了另外一種消息通信機制:消息隊列。其和郵箱類似,均用于任務見消息的傳輸,但其支持的消息大小由用戶指定,可以超過4字節(jié)。

消息隊列可以存放多條消息,發(fā)消息的任務負責將消息發(fā)送至隊列,接收消息的任務負責從隊列中提取消息。AWorks提供了使用消息隊列的幾個宏,宏的原型詳見表10.9。

表10.9 消息隊列相關(guān)的宏(aw_msgq.h)

1. 定義消息隊列實體

AW_MSGQ_DECL()和

AW_MSGQ_DECL_STATIC()宏均用于定義一個消息隊列實體,為消息隊列分配必要的內(nèi)存空間,包括用于存儲消息的空間。它們的原型為:

其中,參數(shù)msgq為消息隊列實體的標識名。msg_num和msg_size用于分配存儲消息的空間,msg_num表示消息的最大條數(shù),msg_size表示每條消息的大?。ㄗ止?jié)數(shù))。用于存儲消息的總內(nèi)存大小即為:msg_num×msg_size。

兩個宏的區(qū)別在于,AW_MSGQ_DECL_STATIC() 在定義消息隊列所需內(nèi)存時, 使用了關(guān)鍵字static ,如此一來, 便可以將消息隊列實體的作用域限制在模塊內(nèi)(文件內(nèi)), 從而避免模塊之間的消息隊列命名沖突,同時,還可以在函數(shù)內(nèi)使用本宏定義消息隊列實體。

如使用AW_MSGQ_DECL()定義一個標識名為msgq_test的消息隊列實體,消息的最大數(shù)目為10,每條消息為一個int類型數(shù)據(jù),則消息的大小為4個字節(jié)(32位平臺中),其范例程序詳見程序清單10.63。

程序清單10.63 定義消息隊列實體的范例程序

通常,當每條消息為一個int類型的數(shù)據(jù)時,其長度最好使用sizeof表示,其范例程序詳見程序清單10.64。

程序清單10.64 定義消息隊列實體的范例程序

使用AW_MSGQ_DECL()定義消息隊列實體時,可以將消息隊列的實體嵌入到另一個數(shù)據(jù)結(jié)構(gòu)中,其范例程序程序清單10.65。

程序清單10.65 將消息隊列實體嵌入到結(jié)構(gòu)體中

也可以使用AW_MSGQ_DECL_STATIC()定義一個標識名為msgq_test的消息隊列實體,其范例程序詳見程序清單10.66。

程序清單10.66 定義消息隊列實體(靜態(tài))的范例程序

2. 初始化消息隊列

定義消息隊列實體后,必須使用

AW_MSGQ_INIT()初始化后才能使用。其原型為:

其中,msgq為由AW_MSGQ_DECL() 或

AW_MSGQ_DECL_STATIC()定義的消息隊列。msg_num表示消息隊列可以存儲的消息條數(shù),其值必須與定義消息隊列實體時的msg_num相同。msg_size表示每條消息的大?。ㄗ止?jié)數(shù)),其值必須與定義消息隊列實體時的msg_size相同。

options為消息隊列的選項,其決定了阻塞于此消息隊列(等待消息中)的任務的排隊方式,可以按照任務優(yōu)先級或先進先出的順序排隊,它們對應的宏詳見表10.10。

表10.10 消息隊列初始化選項宏(aw_msgq.h)

注意,前面講述了三種信號量,在初始化時同樣可以通過選項指定阻塞于信號量的任務的排隊方式,分為按照優(yōu)先級和先進先出兩種方式,它們對應的宏名分別為AW_SEM_Q_PRIORITY和AW_SEM_Q_FIFO。與消息隊列的命名不同,不可混用。

通常排隊方式都選擇按照優(yōu)先級排隊,初始化消息隊列的范例程序詳見程序清單10.67。

程序清單10.67 初始化消息隊列的范例程序

AW_MSGQ_INIT()用于初始化一個消息隊列實體,初始化完畢后,將返回該消息隊列的ID,其類型為aw_msgq_id_t。定義一個該類型的變量保存返回的ID如下:

aw_msgq_id_t的具體定義用戶無需關(guān)心,該ID可作為后文介紹的其它消息隊列相關(guān)接口函數(shù)的參數(shù),用于指定要操作的消息隊列。特別地,若返回ID的值為NULL,表明初始化失敗。一般地,若無特殊需求,不會使用該ID,可以不用保存該ID。

3. 從消息隊列中獲取一條信息

從消息隊列中獲取一條消息的宏原型為:

其中,msgq為由AW_MSGQ_DECL() 或

AW_MSGQ_DECL_STATIC()定義的消息隊列。p_buf指向用于保存消息的緩沖區(qū),消息成功獲取后,將存儲到p_buf指向的緩沖區(qū)中。nbytes指定了緩沖區(qū)的大小,緩沖區(qū)大小必須能夠容納一條消息,其值不得小于定義消息隊列實體時指定的一條消息的長度,通常情況下,nbytes與一條消息的長度是相等的,例如,msgq_test中的消息長度為4字節(jié),則nbytes的值也為4,即p_buf指向的緩存區(qū)大小為4字節(jié)。timeout指定了超時時間。該宏的返回值為aw_err_t類型的標準錯誤號。注意,由于中斷服務程序不能被阻塞,因此,該函數(shù)禁止在中斷中調(diào)用。

如果消息隊列不為空,包含有效的消息,則本次操作將成功獲取到一條消息,同時,會從消息隊列中將該條消息刪除,有效消息的條數(shù)減1。此時,AW_MSGQ_RECEIVE()的返回值為:AW_OK。

如果消息隊列為空,沒有任何有效的消息,則不能立即成功獲取到消息,接下來具體的行為將由超時時間timeout的值決定。

(1)若timeout的值為

AW_MSGQ_WAIT_FOREVER。則任務會阻塞于此,一直等待,直到消息隊列中有可用的消息,即其它任務或中斷發(fā)送了消息。范例程序詳見程序清單10.68。

程序清單10.68 永久阻塞等待消息隊列的范例程序

(2)若timeout的值為AW_MSGQ_NO_WAIT。則任務不會被阻塞,立即返回,但不會成功獲取到消息,此時,AW_MSGQ_RECEIVE()的返回值為:-AW_EAGAIN(表示當前資源無效,需要重試)。范例程序詳見程序清單10.69。

程序清單10.69 不阻塞等待消息隊列的范例程序

(3)若tiemout的值為一個正整數(shù),則表示最長的等待時間(單位為系統(tǒng)節(jié)拍),任務會阻塞于此,在timeout規(guī)定的時間內(nèi),若成功獲取到一條消息,則AW_MSGQ_RECEIVE()的返回值為:AW_OK;若在timeout規(guī)定的時間內(nèi),沒有獲取到消息,則返回值為-AW_ETIME(表明超時)。范例程序詳見程序清單10.33。

程序清單10.70 等待消息隊列的超時時間為500ms的范例程序

4. 發(fā)送一條消息到消息隊列中

發(fā)送消息到消息隊列中的宏原型為:

其中,msgq為由AW_MSGQ_DECL() 或

AW_MSGQ_DECL_STATIC()定義的消息隊列。p_buf指向待發(fā)送的消息緩沖區(qū)。nbytes為消息緩沖區(qū)的大小,消息緩沖區(qū)的長度不得不得大于定義消息隊列實體時指定的一條消息的長度,通常情況下,nbytes與一條消息的長度是相等的,例如,msgq_test中的消息長度為4字節(jié),則nbytes的值也為4,即p_buf指向的緩存區(qū)大小為4字節(jié)。timeout指定了超時時間。priority指定了消息的優(yōu)先級。該宏的返回值為aw_err_t類型的標準錯誤號。

若消息隊列未滿,可以繼續(xù)存儲消息,則該消息發(fā)送成功,消息隊列的有效消息條數(shù)加1。此時,AW_MSGQ_SEND()的返回值為:AW_OK。

若消息隊列已滿,暫時不能繼續(xù)存儲消息,則不能立即成功發(fā)送消息,接下來具體的行為將由超時時間timeout的值決定。

(1)若timeout的值為

AW_MSGQ_WAIT_FOREVER。則任務會阻塞于此,一直等待,直到消息成功放入消息隊列。即其它任務從消息隊列中獲取了消息,消息隊列留出空閑空間。范例程序詳見程序清單10.71。

程序清單10.71 永久阻塞等待消息隊列的范例程序

注意,priority參數(shù)指定了消息的優(yōu)先級,其可能的取值有兩個:AW_MSGQ_PRI_NORMAL和AW_MSGQ_PRI_URGENT,分別表示普通優(yōu)先級和緊急優(yōu)先級。一般地,均使用普通優(yōu)先級,此時,新的消息按照隊列的組織形式,放在隊列的尾部,將最后被取出。若使用緊急優(yōu)先級,則新的消息放在隊列的頭部,將在下次從消息隊列中獲取消息時被取出。

(2)若timeout的值為AW_MSGQ_NO_WAIT。則任務不會被阻塞,立即返回,但不會成功發(fā)送消息,此時,AW_MSGQ_SEND()的返回值為:-AW_EAGAIN(表示當前資源無效,需要重試)。范例程序詳見程序清單10.72。

程序清單10.72 不阻塞等待消息隊列的范例程序

(3)若tiemout的值為一個正整數(shù),則表示最長的等待時間(單位為系統(tǒng)節(jié)拍),任務會阻塞于此,在timeout規(guī)定的時間內(nèi),成功發(fā)送了消息,則AW_MSGQ_SEND()的返回值為:AW_OK;若在timeout規(guī)定的時間內(nèi),沒有成功發(fā)送消息,則返回值為-AW_ETIME(表明超時)。范例程序詳見程序清單10.73。

程序清單10.73 等待消息隊列的超時時間為500ms的范例程序

注意,在中斷服務程序中(如按鍵回調(diào)函數(shù)),可以使用該接口發(fā)送消息至消息隊列,這是中斷和任務之間很重要的一種通信方式:即在中斷中發(fā)送消息,在任務中接收消息并處理,從而減小中斷服務程序的時間。但是,由于中斷不能被阻塞,因此,當在中斷中發(fā)送消息時,timeout標志只能為AW_MSGQ_NO_WAIT。在這種應用中,為了避免消息丟失,應該盡可能避免消息隊列被填滿,可以通過增加消息隊列的大小以及提高處理消息任務的優(yōu)先級,使消息隊列中的消息被盡快處理。

5. 終止消息隊列

當一個消息隊列不再使用時,可以終止該消息隊列,宏原型為:

其中,msgq為由AW_MSGQ_DECL() 或 AW_MSGQ_DECL_STATIC()定義的消息隊列。當消息隊列被終止后,若當前系統(tǒng)中還存在等待該消息隊列的任務,則任何等待此消息隊列的任務將會解阻塞, 并返回-AW_ENXIO(表明資源已不存在),其范例詳見程序清單10.74。

程序清單10.74 終止消息隊列的范例程序

在程序清單10.62中,使用了動態(tài)內(nèi)存分配來管理消息的存儲空間,為了避免使用動態(tài)內(nèi)存分配,可以使用消息隊列,將每條消息的長度定義為8,以便存儲按鍵編碼和按鍵狀態(tài)。范例程序詳見程序清單10.75。

程序清單10.75 消息隊列使用范例程序

該程序與程序清單10.62所示的程序分別使用郵箱和消息隊列實現(xiàn)了相同的功能。消息隊列避免了使用動態(tài)內(nèi)存分配,在定義消息隊列實體時,就完成了相關(guān)內(nèi)存的靜態(tài)分配。避免了使用動態(tài)內(nèi)存分配的種種缺點。但是,當使用消息隊列時,若定義的容量過大,可能造成不必要的內(nèi)存浪費。

此外,郵箱和消息隊列發(fā)送消息的方式是不同的,對于郵箱,其僅僅發(fā)送了消息的首地址,接收者接收到地址后,直接從地址中取出相應的消息,這種方式下,消息傳遞的效率很高。

但對于消息隊列,發(fā)送消息時,是將整個消息內(nèi)容(如按鍵編碼和按鍵狀態(tài))拷貝到消息隊列的緩沖區(qū)中,接收消息時,再將存儲在消息隊列緩沖區(qū)中的消息完整的拷貝到用戶緩沖區(qū)中。由此可見,一次消息傳輸存在兩次消息內(nèi)容的完全拷貝過程,這種傳輸方式效率很低,特別是對于一條消息很大的情況。

因此,建議當一條消息很大時,使用郵箱;而當一條消息較小時,消息的拷貝對性能的影響較弱,則使用消息隊列更加方便快捷。

當使用郵箱時,為了避免使用常規(guī)動態(tài)內(nèi)存分配方法造成內(nèi)存碎片、內(nèi)存泄漏、分配效率等問題??梢允褂肁Works提供的靜態(tài)內(nèi)存池管理技術(shù),其為了避免內(nèi)存碎片和分配效率等問題,將每次分配內(nèi)存的大小設定為一個固定值。內(nèi)存池管理技術(shù)將在“內(nèi)存管理”章節(jié)中詳細介紹。

10.5 自旋鎖

互斥信號量用于任務間對共享資源的互斥訪問,在一個任務獲取互斥信號量時,若互斥信號量無效,需要等待時,則任務會主動釋放CPU,內(nèi)核調(diào)度器進而調(diào)度CPU去執(zhí)行其它任務,當互斥信號量恢復有效時,再重新調(diào)度CPU繼續(xù)執(zhí)行之前的任務。這樣,在任務等待互斥信號量有效的這段時間里,CPU可以被充分利用,去處理其他任務。

但是,調(diào)度過程是需要耗費一定時間的,有些時候,對共享資源的訪問可能非常簡單,消耗CPU的時間很短,也就是說,一個任務占用共享資源的時間非常短,其獲得互斥信號量后很快就會釋放。這種情況下,當一個任務獲取互斥信號量時,即使當前的信號量無效,也意味著該信號量很快就會被釋放,變?yōu)橛行?。若任務在此時釋放CPU,執(zhí)行任務調(diào)度,很可能在任務調(diào)度過程中,信號量就被釋放了,系統(tǒng)又不得不在調(diào)度結(jié)束后重新將CPU再調(diào)度回來,這使系統(tǒng)在任務調(diào)度上花費了太多的時間成本。這種情況下,任務不釋放CPU將是一種更好的選擇,可以提高任務執(zhí)行的效率。

AWorks提供了自旋鎖,所謂“自旋”,就是一個“自我輪詢檢查”,當獲取自旋鎖時,若自旋鎖處于無效(被鎖)狀態(tài),則不會釋放CPU,而是輪詢檢查自旋鎖,直到自旋鎖被釋放(解鎖)。檢查到自旋鎖被釋放后,立即獲取該自旋鎖,使之成為鎖住狀態(tài),接著盡快迅速完成對共享資源的訪問,訪問結(jié)束后,釋放自旋鎖。

由于當自旋鎖不可用時,任務將一直循環(huán)檢查自旋鎖的狀態(tài)直到可用而不會釋放CPU, CPU在輪詢等待期間不做任何其它有效的工作,因此,只有在共享資源占用時間極短的情況下,使用自旋鎖才是合理的。否則,應該使用互斥信號量。需要特別注意的是,自旋鎖不支持遞歸使用。

AWorks提供了使用自旋鎖的通用接口,接口的原型詳見表10.11。

表10.11 自旋鎖通用接口(aw_spinlock.h)

在AWorks中,自旋鎖可以在中斷中使用,因而在接口命名中,含有“isr”關(guān)鍵字。之所以可以在中斷中使用,是由于在獲取到自旋鎖后,會關(guān)閉總中斷,釋放自旋鎖時,再打開總中斷,使得在訪問自旋鎖保護的共享資源時,可以獨占CPU,保證其不會被中斷打斷。否則,若任務在獲取到自旋鎖還未釋放時被中斷打斷,在中斷上下文中再次獲取自旋鎖將造成“死鎖”:任務未釋放自旋鎖,中斷只能等待;中斷占用了CPU,任務只有等待中斷結(jié)束返回后才能繼續(xù)執(zhí)行,以釋放自旋鎖。

換句話說,在AWorks中,自旋鎖可以在中斷中使用,任務和中斷對共享資源的訪問是互斥的,當任務訪問共享資源時,中斷會被關(guān)閉,以實現(xiàn)互斥。

1. 定義自旋鎖實體

在使用自旋鎖前,必須先使用aw_spinlock_isr_t類型定義自旋鎖實體,該類型在aw_spinlock.h中定義,具體類型的定義用戶無需關(guān)心,僅需使用該類型定義自旋鎖實體,即:

其地址即可作為各個接口中p_lock參數(shù)的實參傳遞,表示具體要操作的自旋鎖。

2. 初始化自旋鎖

定義自旋鎖實體后,必須使用該接口初始化后才能使用。其原型為:

其中,p_lock指向待初始化的自旋鎖。flags為自旋鎖的標志,當前無任何可用標志,該值需設置為0。初始化自旋鎖的范例程序詳見程序清單10.76。

程序清單10.76 初始化自旋鎖

3. 獲取自旋鎖

獲取自旋鎖的函數(shù)原型為:

其中,p_lock指向需要獲取的自旋鎖。若自旋鎖有效,則獲取成功,并將自旋鎖設置為無效狀態(tài);若自旋鎖無效,則會輪詢等待(不會像互斥信號量那樣釋放CPU),直到自旋鎖有效(占用該鎖的任務釋放自旋鎖)后返回。該接口可以在中斷上下文中使用。獲取自旋鎖的范例程序詳見程序清單10.77。

程序清單10.77 獲取自旋鎖

4. 釋放自旋鎖

釋放自旋鎖的函數(shù)原型為:

其中,p_lock指向需要釋放的自旋鎖。自旋鎖的獲取和釋放操作應該成對出現(xiàn),即在一個任務(或中斷上下文)中,先獲取自旋鎖,再訪問由該自旋鎖保護的共享資源,訪問結(jié)束后釋放自旋鎖。不可一個任務(或中斷上下文)僅獲取自旋鎖,另一個任務(或中斷上下文)僅釋放自旋鎖。釋放自旋鎖的范例程序詳見程序清單10.78。

程序清單10.78 釋放自旋鎖

在互斥信號量的范例程序中(詳見程序清單10.25),使用了兩個任務互斥訪問共享資源(調(diào)試串口)進行了舉例說明,由于調(diào)試串口輸出信息的速度慢,輸出一條字符串信息耗時往往在毫秒級別,因此,這種情況下,使用自旋鎖是不合適的。一般來講,操作硬件設備都不建議使用自旋鎖,自旋鎖往往用于互斥訪問類似于全局變量的共享資源。

例如,有兩個任務task1和task2。在task1中,每隔50ms對全局變量進行加1操作,在task2中,檢查全局變量的值,若達到10,則將全局變量的值重置為0,并翻轉(zhuǎn)一次LED。

由于兩個任務均需對全局變量進行操作,為了避免沖突,需要兩個任務互斥訪問該全局變量,顯然,加值操作是非??斓模加脮r間極短,可以使用自旋鎖實現(xiàn)互斥訪問,范例程序詳見程序清單10.79。

程序清單10.79 自旋鎖使用范例程序

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 周立功
    +關(guān)注

    關(guān)注

    38

    文章

    130

    瀏覽量

    37438
  • 致遠電子
    +關(guān)注

    關(guān)注

    13

    文章

    402

    瀏覽量

    31201
  • AWorks
    +關(guān)注

    關(guān)注

    1

    文章

    16

    瀏覽量

    5662

原文標題:AWorks軟件篇 — 實時內(nèi)核(郵箱、消息隊列和自旋鎖)

文章出處:【微信號:ZLG_zhiyuan,微信公眾號:ZLG致遠電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    深度解析自旋自旋的實現(xiàn)方案

    入場券自旋和MCS自旋都屬于排隊自旋(queued spinlock),進程按照申請
    發(fā)表于 09-19 11:39 ?4263次閱讀
    深度解析<b class='flag-5'>自旋</b><b class='flag-5'>鎖</b>及<b class='flag-5'>自旋</b><b class='flag-5'>鎖</b>的實現(xiàn)方案

    Linux驅(qū)動開發(fā)筆記-自旋和信號量

    :如果在寫代碼時,有以上的競態(tài)發(fā)生,一定要注意進行互斥訪問7.解決競態(tài)的方法:中斷屏蔽原子操作自旋信號量如何使用以上4個機制呢?1.中斷屏蔽解決哪些情況的競態(tài):進程和進程的搶占中斷和進程中斷和中斷
    發(fā)表于 08-30 18:08

    信號量、互斥、自旋

    信號量、互斥自旋http://bbs.edu118.com/forum.php?mod=viewthread&tid=488&fromuid=231(出處: 信盈達IT技術(shù)社
    發(fā)表于 08-29 09:48

    Linux內(nèi)核同步機制的自旋原理是什么?

    自旋是專為防止多處理器并發(fā)而引入的一種,它在內(nèi)核中大量應用于中斷處理等部分(對于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關(guān)閉中斷的方式,即在標志寄存器中關(guān)閉/打開中斷標志位,不需要自旋
    發(fā)表于 03-31 08:06

    怎么在atmega128中實現(xiàn)自旋?

    什么是自旋?有哪些缺陷?怎么在atmega128中實現(xiàn)自旋?
    發(fā)表于 01-24 06:54

    CapTIvate技術(shù)中軟件庫的使用方法介紹

    CapTIvate Technology軟件設計快速指南(七) - CapTIvate軟件庫的使用方法
    的頭像 發(fā)表于 04-16 06:50 ?2667次閱讀
    CapTIvate技術(shù)中<b class='flag-5'>軟件</b>庫的<b class='flag-5'>使用方法</b>介紹

    信號量和自旋

    ------------------------------------------------------需求 ??? ??? ??? ??? ????? 建議的加鎖方法低開銷加鎖 ??? ??? ?????? ?優(yōu)先使用自旋
    發(fā)表于 04-02 14:43 ?776次閱讀

    Linux 自旋spinlock

    ,所以同一時刻只能有一個任務獲取到。 內(nèi)核當發(fā)生訪問資源沖突的時候,通常有兩種處理方式: 一個是原地等待 一個是掛起當前進程,調(diào)度其他進程執(zhí)行(睡眠) 自旋 Spinlock 是內(nèi)核中提供的一種比較常見的
    的頭像 發(fā)表于 09-11 14:36 ?1989次閱讀

    自旋的發(fā)展歷史與使用方法

    自旋是Linux內(nèi)核里最常用的之一,自旋的概念很簡單,就是如果加鎖失敗在等時是使用休眠等
    的頭像 發(fā)表于 08-08 08:51 ?1590次閱讀

    自旋和互斥的區(qū)別有哪些

    自旋 自旋與互斥很相似,在訪問共享資源之前對自旋
    的頭像 發(fā)表于 07-21 11:19 ?9016次閱讀

    隊列解決的問題

    為什么需要無隊列隊列解決了什么問題?無隊列解決了
    的頭像 發(fā)表于 11-10 15:33 ?743次閱讀
    無<b class='flag-5'>鎖</b><b class='flag-5'>隊列</b>解決的問題

    如何用C++11實現(xiàn)自旋

    下面我會分析一下自旋,并代碼實現(xiàn)自旋和互斥的性能對比,以及利用C++11實現(xiàn)自旋
    的頭像 發(fā)表于 11-11 16:48 ?1240次閱讀
    如何用C++11實現(xiàn)<b class='flag-5'>自旋</b><b class='flag-5'>鎖</b>

    互斥自旋的區(qū)別 自旋臨界區(qū)可以被中斷嗎?

    互斥自旋的區(qū)別 自旋臨界區(qū)可以被中斷嗎? 互斥
    的頭像 發(fā)表于 11-22 17:41 ?659次閱讀

    自旋和互斥的使用場景是什么

    自旋和互斥是兩種常見的同步機制,它們在多線程編程中被廣泛使用。在本文中,我們將介紹自旋和互斥
    的頭像 發(fā)表于 07-10 10:05 ?529次閱讀

    互斥自旋的實現(xiàn)原理

    互斥自旋是操作系統(tǒng)中常用的同步機制,用于控制對共享資源的訪問,以避免多個線程或進程同時訪問同一資源,從而引發(fā)數(shù)據(jù)不一致或競爭條件等問題。 互斥(Mutex) 互斥
    的頭像 發(fā)表于 07-10 10:07 ?277次閱讀