消息隊列在RTOS的應用
傳說互聯網應用有兩大利器,一個是緩存,另一個就是消息隊列。 一直相對消息隊列做一下梳理,希望早日另有成文。 一葉知秋,實際上消息隊列在嵌入式系統中同樣有著廣泛的應用。 近來致力于IoT和智能硬件,現學習一下消息隊列在RTOS中的應用場景。
RTOS是一個管理CPU的軟件, 即微處理單元(MPU) , 還可能管理高效的DSP。 大多數 RTOS 內核是用 c 語言編寫的, 同時需要用匯編語言編寫一小部分代碼來適應不同的 CPU 架構。一個 RTOS 內核為開發者提供了許多有用的服務, 如多任務處理、中斷管理、通過消息隊列、信號量、資源管理、時間管理、內存分區管理等等。
應用程序或者最終地產品基本上會劃分為多個任務, 每個任務負責應用程序的一部分。 一個任務就是一個簡單的程序, 它有自己的 CPU使用時間。 根據任務的重要性, 每個任務都被賦予了優先級。
RTOS中的消息隊列
圖1 消息隊列是用于將內容傳遞給任務的內核對象
如圖1所示, 消息隊列是一個核心對象(即數據結構) , 通過這個對象, 消息從中斷服務例程(ISR)或任務發送到另一個任務。 應用程序可以有任意數量的消息隊列, 每一個都有自己的目的和用途。 例如, 消息隊列可以用來將從通信接口 ISR 接收到的數據包傳遞給一個任務, 而 ISR 則負責處理數據包。 另一個隊列可以用來將內容傳遞給顯示任務, 該任務將負責正確地更新顯示的內容。
消息通常是指向包含實際消息存儲區域的void指針。 這些指針可以指向任何東西, 甚至可以指向接收任務執行的函數。 因此, 它的實際含義取決于應用程序。 每個消息隊列都可以根據它將保存的存儲量進行配置。 消息隊列可以配置為保存單個消息或者 n 個消息。 隊列的大小取決于應用程序以及消費者的處理速度。
如果一個任務等待一條消息, 并且隊列中沒有消息, 則該任務將被掛起, 直到有消息發送到隊列中。 等待任務不消耗 CPU 時間, RTOS 可以運行其他任務。 如圖1所示, 掛起的任務可以指定超時。 如果在指定時間內未收到消息, 則當該任務成為***優先任務時, 將允許任務恢復執行(即 unblock)。 當任務執行時, 基本上會被告知它恢復的原因是因為超時。
消息隊列通常作為先入先出(FIFO)實現, 這意味著收到的***個消息將是從隊列中提取的***個消息。 然而, 有些內核允許發送被認為比其他內核更重要的消息, 從而排在隊列的首位。 換句話說, 在"先入先出"順序中, 使該消息成為任務提取的***條信息。
消息隊列的另一個重要方面是, 消息本身需要保持從發送到處理的時間范圍。 這意味著不能將指針傳遞給棧變量等等。 為了將消息保持在作用域中, 通常會填充一個結構, 并從這些消息池中獲取, 如圖2所示。
圖2 消息池的存儲區域
發送消息的 ISR 或任務將從池中獲取結構, 填充結構, 并將指針指向隊列的結構。 接收任務將從隊列中提取指針, 處理結構, 完成后將結構返回到池中。 當然, 發送方和接收方都需要使用相同的池, 除非數據結構中的字段指示使用了哪個池。
在 RTOS 中的許多消息隊列實現中, 如隊列已滿, 則發送到隊列的消息將被丟棄。 通常這不是一個問題, 應用程序的邏輯可以從這種情況中恢復。 然而, 如圖3所示, 實現一個發送任務會發送消息的機制是相當容易的:
圖3 如果隊列已滿, 則阻止發送
- 計數信號的初始化值與隊列可接受的***條目數相對應
- 發送任務在允許將消息發送到隊列之前檢查信號量。 如果信號量值為零, 則發送方等待。
- 如果值為非零, 則信號量減少, 發送方將消息發送到隊列中
- 消息的接收方像往常一樣將消息隊列分隔開來
- 當收到消息時, 接收這將指針從隊列中取出并向信號量發出信號, 表示隊列中的條目已被釋放
如圖所示, 這個機制只適用于兩個任務, 因為 ISR 不允許在信號量上分配信號。
消息隊列的典型用法
圖4顯示了消息隊列的不同用法:
1-4. 消息隊列通常用于從 ISR 發送消息或將任務發送到另一個任務, 如前面所討論的那樣
5.但是, 如果消息符合指針的大小, 則不必發送實際消息及分配存儲區域。 例如, 如果一個32位指針, 那么可以將模擬轉換器(ADC)從一個12位 ADC 讀取到一個指針, 并通過消息隊列發送,只要接收這知道將值返回整數即可, 這是完全合法的
6-7 一個任務如果知道這些消息將不會發送給它的,可以使用超時機制在一定的時間內延遲自己。 在這種情況下, 一個能夠保存單個條目的隊列就足夠了。 事實上, 如果另一個任務或 ISR 發送消息, 那么延遲將被終止, 這可能也是想要實現的行為。
8 消息隊列可以作為一個信號量來簡單地向事件發生的任務發出信號。 在這種情況下, 信息可以是任何東西。 隊列的大小取決于應用程序需要緩沖多少信號。
9-10 消息隊列也可以用作二進制信號量或用于資源共享的計數信號量。 對于二進制信號量, 隊列將包含單個消息, 并在隊列中放置消息(任何值)。 為了訪問資源, 可以在隊列上分配一個任務。 如果隊列中有消息, 則該任務將獲得對資源的訪問。 一旦使用了資源, 隊列就會被發布, 從而根據需要放棄資源, 供其他任務使用。 同樣的機制也適用于使用 n 個資源實現計數信號, 隊列將預先填入n個虛擬消息。
11.消息實際上可以用來模擬事件標志, 其中32位指針大小的變量中,每一位可以代表一個事件
12.消息隊列可以用來實現棧結構,這基本上是 LIFO 機制的另一個用途。
總而言之,消息隊列有許多不同方式的使用場景。 事實上, 通過消息隊列,可以編寫相當復雜的應用程序。 使用消息隊列可以減少代碼的大小, 可以被模擬(信號、時間延遲和事件標志)許多其他的服務。
【本文來自51CTO專欄作者“老曹”的原創文章,作者微信公眾號:喔家ArchiSelf,id:wrieless-com】