目錄
本學筆記基於zephyr 工程版本 2.2.99,主機環境爲ubuntu18.04,開發平臺 nrf52840dk_nrf52840
摘要
FIFO是一個內核對象,實現了傳統的先進先出(first in, first out)隊列,允許線程和ISR添加或者移除任意大小的數據項。
1 概念
任意數量的FIFO可以被定義。引用fifo時,使用的是FIFO定義的內存地址,同樣只要你的內存允許,就可以隨便定義。
FIFO有如下關鍵屬性:
一個queue,他存儲已經被添加但是還沒有被移除的數據項。這個queue是用簡單的鏈表實現的(實際上就是一個由鏈表實現的隊列)。FIFO的功能是基於這個先進先出的queue實現的,FIFO就相當於queue的head。後面還會講LIFO,也是一個queue,但是是一個後進先出的queue。
一個FIFO必須先初始化在使用,初始化設置這個隊列爲空。
FIFO的數據項對必須保持字(word,cortext-M是4字節)對齊,這個數據項的第一個字節被內核使用,用於存儲在queue中下一個數據項的內存地址。因此,一個有N字節應用數據的數據項,需要佔用N+4(or N+8, 64位系統中word是8字節)內存空間。如果使用k_fifo_alloc_put()個函數添加一個數據項到一個隊列,就不需要指向下一個地址的保留word。因爲這個函數會從線程的資源池中分配臨時的內存用於存儲下一個數據項的地址。
一個數據項可以被ISR或者線程添加,如果有正在等待數據項的線程(就是讀取隊列,隊列中沒有數據項,線程休眠了),那麼添加一個數據項之後,數據項不被添加到FIFO的quue中,而是直接給等待的線程。也就是數據項不是由發送線程存儲到FIFO的queue中,然後再由等在線程從queue中取出。而是直接從發送線程傳給因爲接收而等待的線程,不會經過queue的。相反,如果沒有等待的接收線程,那麼纔會添加數據項到FIFO的queue。
一個數據可以被一個線程移除(也就是取出),如果FIFO中沒有數據元素,那麼線程可以選擇等待FIFO不爲空(就是說線程獲取不到FIFO進入睡眠,當FIFO不爲空的時候再喚醒)。可以有多個線程同時等待一個空的FIFO。當有數據項添加到FIFO中,那個優先級最高等待時間最長的線程,會優先喚醒去讀取數據。
2 實現
2.1 定義FIFO
使用struct k_fifo類型定義一個FIFO變量。使用之前必須用k_fifo_init()進行初始化:
struct k_fifo my_fifo;
k_fifo_init(&my_fifo);
或者,在編譯時使用K_FIFO_DEFINE宏定義和初始化一個FIFO:
K_FIFO_DEFINE(my_fifo);
2.2 寫數據項到FIFO
可以調用k_fifo_put()函數,向FIFO中添加一個數據項。
下面的示例發送數據到一個或多個用戶線程:
struct data_item_t {
void *fifo_reserved; /* 1st word reserved for use by fifo */
...
};
struct data_item_t tx_data;
void producer_thread(int unused1, int unused2, int unused3)
{
while (1) {
/* create data item to send */
tx_data = ...
/* send data to consumers */
k_fifo_put(&my_fifo, &tx_data);
...
}
}
也可以使用k_fifo_alloc_put()函數添加,那麼添加的數據項內存中第一個word也是有效的,不會被內核佔中。
2.3 從FIFO中讀數據
可以調用k_fifo_get()從FIFO中讀取一個數據項:
void consumer_thread(int unused1, int unused2, int unused3)
{
struct data_item_t *rx_data;
while (1) {
rx_data = k_fifo_get(&my_fifo, K_FOREVER);
/* process fifo data item */
...
}
}
3 參考鏈接
https://docs.zephyrproject.org/latest/reference/kernel/data_passing/fifos.html