天天看點

4-FreeRTOS隊列、互斥、信号量

1-隊列

隊列(我對隊列的了解就是上體育課,排隊這種)是任務之間通信的一種方式。隊列可以用于任務和任務之間或者中斷和任務之間消息的接收與發送。在多數情況下,他們消息緩沖是按照FIFO(先進先出)原則。也就是最新的資料在上一個資料的後面,換句話說就是,類似于吃羊肉串,比如第一個串起來的,就第一個被吃掉。

下圖是隊列的資料讀寫:

4-FreeRTOS隊列、互斥、信号量

FreeRTOS隊列使用者通常情況下會把靈活性與簡單性相結合,但是這種類型通常是屬于互斥的。

消息是通過複制的方式經過隊列來進行發送,這說明資料的本身被複制到隊列中,而不是隊列對存儲資料的一直引用。這種方式相對來說是較為友好的。主要原因以下幾點。

  • 如果已經包含在C變量之中(整型、結構體短小的)是可以直接發送到隊列中的。不需要為消息進行緩沖區的配置設定。

    也可以直接從隊列中把消息讀取到C變量中。

    除此之外,這種隊列發送方式,即使發送的消息仍保留在隊列之中,也是允許發送任務立即覆寫發送到隊列的緩沖區或者變量。由于變量中包含的資料被複制到隊列中,是以變量本身可以重用。無論任務是接收消息還是發送消息,不需要商定是哪個任務接收消息,以及哪個任務負責發送消息。

  • 使用通過複制傳遞資料的隊列并不會阻止使用隊列通過引用傳遞資料。當消息的大小達到某個點時,将整個消息位元組複制到隊列中是不現實的,則定義隊列以儲存指針,隻将指向消息的指針複制到隊列中。
  • 核心完全負責配置設定用作隊列存儲區域的記憶體
  • 可以通過定義結構體成員來儲存大小不一的資訊,然後再通過隊列進行消息的發送和接收。
  • 單個隊列可用于接收不同的消息類型和來自多個位置的消息,方法是将隊列定義為包含一個結構,該結構具有一個成員儲存消息類型,另一個成員儲存消息資料(或指向 消息資料)。 如何發送資料取決于消息類型。 這正是管理 FreeRTOS-Plus-UDP IP 堆棧的任務能夠使用單個隊列接收 ARP 計時器事件通知、從以太網硬體接收的資料包、從應用程式接收的資料包、網絡傳輸事件等。
  • 這種方式的實作适合受到存儲器保護的記憶體環境中使用。在受保護記憶體區域的任務可以将資料傳遞給限制在不同受保護記憶體區域的任務,因為通過調用隊列發送函數來調用 RTOS 會提高微控制器的特權級别。 隊列存儲區隻能由 RTOS 通路(具有完全權限)。

提供了一個單獨的API中斷供内部使用。

提供了一個單獨的 API 供在中斷内部使用。 将 RTOS 任務使用的 API 與中斷服務例程使用的 API 分開意味着 RTOS API 函數的實作不會帶來每次執行時檢查其調用記憶體的管理。 使用單獨的中斷 API 還意味着,在大多數情況下,與替代 RTOS 産品相比,建立 RTOS 感覺中斷服務例程對最終使用者來說更簡單。

1.1 隊列阻塞

允許隊列API函數指向阻塞

當任務從一個空的隊列讀取的時候,這個任務将被設定為阻塞狀态,因為這樣不會消耗任何的CPU以及時間,而且其他的任務是可以運作的,直到隊列中有資料為止,或者阻塞時間結束。

當任務嘗試寫入完整隊列時,該任務将處于 阻塞的狀态(是以它不消耗任何 CPU 時間,并且可以運作其他的任務) 直到隊列中的空間可用,或者阻塞結束。

如果在同一隊列上有多個阻塞任務,那麼優先級高的将最先解除。

2-FreeRTOS 二進制信号量

二進制信号量的主要用于互斥和同步。

二進制信号量和互斥鎖非常相似,但有一些細微的差別:互斥鎖有優先級繼承機制,而二進制信号量沒有。這使得二進制信号量能夠實作同步(任務之間或任務與中斷之間)有更好選擇,互斥體成為實作簡單互斥較好的選擇。互斥鎖如何被用作互斥機制的描述同樣适用于二進制信号量。

允許信号量的API函數指向阻塞。

阻塞表示當一個任務嘗試“擷取”一個信号量時,如果該信号量不是立即可用的,該任務應該進入阻塞狀态的最大“tick”數。如果有多個任務阻塞在同一個信号量上,那麼優先級最高的任務将是該信号量下次可用時被解除阻塞的任務。

将二進制信号量視為隻能容納一個項目的隊列。是以,隊列隻能為空或已滿(是以為二進制)。任務和中斷 使用隊列并不關心隊列包含什麼 - 他們隻想知道隊列是空的還是滿的。這 可以利用機制來同步(例如)任務與中斷。

考慮任務被用于服務外圍裝置。輪詢外圍裝置将浪費 CPU 資源,并阻止其他任務執行。是以,最好任務大部分時間都處于Blocked狀态(允許其他任務執行),并且隻在實際有事情需要它做的時候才執行它自己。這是通過二進制信号量在嘗試“擷取”信号量時讓任務塊來實作的。然後為外設編寫中斷例程,當外設需要服務時,該任務僅“提供”信号量。任務 總是“擷取”信号量(從隊列中讀取以使隊列為空),但從不“給予”它。中斷總是“給予”信号量(寫入 到隊列以使其滿員),但從不接受它。

任務優先級可以用來確定外設及時獲得服務——有效地生成一個“延遲中斷”方案。(注意,FreeRTOS也有一個内置的延遲中斷機制)。另一種方法是使用隊列來代替信号量。當這完成時,中斷程式可以捕獲與外圍事件相關的資料,并将其發送到一個隊列中給任務。當隊列上的資料變為可用時,該任務解除阻塞,從隊列中檢索資料,然後執行所需的任何資料處理。第二種方案允許中斷保持盡可能短的時間,而所有的後處理都發生在一個任務中。

下圖是使用信号量使任務與中斷同步:中斷隻會“給出”信号量,而任務隻會“擷取”信号量。

4-FreeRTOS隊列、互斥、信号量

3-FreeRTOS計數信号量

就像二進制信号量可以被認為是長度為1的隊列一樣,計數信号量可以被認為是長度大于1的隊列。同樣,信号量并不關心存儲在隊列中的資料,但隻關心隊列是否為空。

計數信号通常用于兩種情況:

  • 事件計數

    在這個使用場景中,事件處理程式将在每次事件發生時“給出”一個信号量(信号量計數值遞增),而處理程式任務将在每次處理事件時“擷取”一個信号量(信号量計數值遞減)。是以,計數值是已經發生的事件數和已經處理的事件數之間的內插補點。在這種情況下,當建立信号量時,計數值應該為零。

  • 資源管理

    在此使用方案中,計數值訓示可用的資源數。要獲得對資源的控制,任務必須首先獲得信号量 - 遞減信号量計數值。當計數值達到零時,沒有可用資源。當任務完成時 它“提供”信号量的資源 - 遞增信号量計數值。在這種情況下,計數值最好為 等于建立信号量時的最大計數值。

4-FreeRTOS互斥鎖

互斥體是包含優先級繼承機制的二進制信号量。而二進制信号量 是實作同步(任務之間或任務與中斷之間)的更好選擇,互斥體是實作同步的更好選擇。

當互斥用于互斥時,互斥就像保護資源的令牌一樣。當一個任務希望通路該資源時,它必須首先獲得(“擷取”)令牌。當它完成了對資源的處理後,它必須“歸還”令牌——允許其他任務有機會通路相同的資源。

互斥體使用相同的信号量通路 API 函數,是以也允許指定塊時間。區塊時間表示“即時報價”的最大數量 如果互斥鎖不能立即可用,則任務在嘗試“擷取”互斥鎖時應進入“阻塞”狀态。與二進制信号量不同但是 - 互斥鎖采用優先級繼承。這意味着,如果高優先級任務在嘗試擷取互斥鎖(令牌)時阻塞 目前由較低優先級的任務持有,則持有令牌的任務的優先級暫時提高到阻止任務的優先級。此機制 旨在確定優先級較高的任務在盡可能短的時間内保持阻塞狀态,進而最大限度地減少“優先級倒置” 這種情況已經發生。

優先級繼承不能解決優先級倒置!在某些情況下,它隻是将其影響降至最低。硬實時應用程式應設計為此類 優先級倒置首先不會發生。

最好不要從中斷中使用互斥:

因為:

  • 它們包括一個優先級繼承機制,隻有在以下情況下才有意義 互斥鎖是從任務中給出和擷取的,而不是中斷。
  • 中斷不能阻塞以等待由 互斥鎖變為可用。
  • 下圖是:互斥鎖對共享資源的通路

5-FreeRTOS 遞歸互斥

遞歸使用的互斥鎖可以被所有者反複“擷取”。在所有者調用之前,互斥鎖不再可用 xSemaphoreGiveRecursive() 表示每個成功的 xSemaphoreTakeRecursive() 請求。例如,如果一項任務成功地“采取”了相同的任務 互斥鎖 5 次,則互斥鎖将不可用于任何其他任務,直到它也“傳回”互斥鎖正好五次。

這種類型的信号燈使用優先級繼承機制,是以“擷取”信号量的任務必須始終“傳回”信号量一次 的 信号燈 它不再需要。

不能從中斷服務例程中使用互斥類型信号量。

不應從中斷中使用互斥體,因為:

  1. 它們包括一個優先級繼承機制,隻有在以下情況下才有意義 互斥鎖是從任務中給出和擷取的,而不是中斷。
  2. 中斷不能阻塞以等待由 互斥鎖變為可用

繼續閱讀