電電俠 ? 坐公交車、辦理業務等經常需要排隊,計算機系統中也有類似的“隊列“概念嗎? ?
當然。隊列被用作進程之間的通信管道,通常是一對一的,如下圖所示。
科科君
任務 A 將信息存入隊列,任務B以先進先出的方式提取信息。隊列通常應足夠大,可以承載許多數據,而不僅僅承載單個數據項。因此,它可以充當緩沖或暫存器,為管道提供靈活性。它的優點是插入和提取功能可以異步進行(只要管道沒有填滿)。它在 RAM 中實現。進程之間傳遞的信息可能是數據本身,也可能是指向數據的指針。指針通常用于在 RAM 存儲受限時處理大量數據。實現隊列的技術有兩種:鏈表類型結構和循環緩沖區。
電電俠 ? 想了解下鏈表的特性。 ? 鏈表的一個非常有用的特性是它的大小不一定是固定的,而是可以根據需要擴大或縮小。 科科君
此外,可以構建非常大的隊列,僅受可用內存空間的限制。但是對于嵌入式系統來說,這些并不是特別的優勢。首先,如果 RAM 有限,則根本不可能構造很大的隊列。其次,處理多個消息的大型FIFO隊列從數據輸入到數據輸出可能會有較長的傳輸延遲,就性能而言,對于許多實時應用可能太慢。因此,用于嵌入式的首選隊列(通道)結構是循環緩沖區,如下圖所示。
電電俠 ? 循環緩存區應該如何設計? ?
循環緩沖區通常設計為使用固定數量的內存空間,用來保存一定數量的數據,如上圖(a) 所示。
科科君
緩沖區大小是在創建時定義的(例如這里是10個數據單位),但在之后是固定的。使其循環的原因是數據單元0是數據單元9的后繼,尋址是使用模9計數器完成的(就像12小時時鐘使用模12計算一樣)。
電電俠 ? 在讀寫操作期間,數據如果在通道中移動,時間開銷增加怎么辦? ?
但是,一般來說這會帶來不可接受的時間開銷。
科科君
這里使用另一種方法,上圖(b)展示了如何使用指針來標識存儲數據的起始和結束位置(“讀取者”和“發送者”)。通過指針,不必在緩沖區中移動數據。插入的數據單元始終位于相同的內存位置,僅需改變指針的值,圖(c)和圖(d)所示。這些指針也可以用來定義隊列滿和隊列空的條件(當它們相等時)。
電電俠 ? 什么情況下任務會掛起? ?
在正常情況下,任務 A 和任務 B異步進行,根據需要從隊列中插入和刪除數據。任務掛起只在兩種情況下發生:隊列滿和隊列空。
科科君
小貼士:內存池和隊列之間有一個重要的區別———內存池讀取數據不會影響內容,但是從隊列讀取時會“消耗”數據,即破壞性操作(實際上這只是概念性看法,讀指針只是移到了下一個位置)。
隊列使用的概要
下方代碼清單給出了隊列使用的概要。 科科君 01 ? ?
/* 基礎API */
/* 1. 創建隊列 */
FOS_CreateQueue(QLength, QItemSize);
/* 2. 從隊列獲取消息 */
FOS_GetFromQueue(QName, AddOfQData, QwaitingTime);
/* 3. 向隊列發送消息 */
FOS_SendToQueue(QName, AddOfQData, QwaitingTime);
02 ? ?
/* 創建一個全局的隊列聯結發送任務A和接收任務B */
/* 使用RTOS提供的數據類型 */
FOS_QName GlobalQA2B;
FOS_QLength QA2Blength = 1;
FOS_ItemSize QA2BItemSize = 4;
GlobalQA2B = FOS_CreateQueue (QA2Blength, QA2BItemSize);
03 ? ?
/* 發送到隊列-任務A */
/* 使用RTOS提供的數據類型 */
long DataForQueueA2B;
const FOS_QwaitTime NoWaiting = 0;
FOS_ QloadStatus QLoadState;
QLoadState = FOS_SendToQueue (GlobalQA2B, &DataForQueueA2B, 0);
04 ? ?
/* 從隊列獲取-任務B */
/* 使用RTOS提供的數據類型 */
long DataFromQueueA2B;
const FOS_QwaitTime NoWaiting = 0;
FOS_QreadStatus QreadState;
審核編輯:黃飛
評論
查看更多