在前一篇文章中【TencentOS tiny學習】源碼分析(3)——隊列
咱們描述了TencentOS tiny的隊列實現,同時也點出了TencentOS tiny的隊列是依賴於消息隊列的,那麼咱們今天來看看消息隊列的實現。函數
其實消息隊列是TencentOS tiny的一個基礎組件,做爲隊列的底層。
因此在tos_config.h
中會用如下宏定義:源碼分析
#if (TOS_CFG_QUEUE_EN > 0u) #define TOS_CFG_MSG_EN 1u #else #define TOS_CFG_MSG_EN 0u #endif
在系統初始化(tos_knl_init()
)的時候,系統就會將消息池進行初始化,其中, msgpool_init()
函數就是用來初始化消息池的,該函數的定義位於 tos_msg.c文件中,函數的實現主要是經過一個for
循環,將消息池k_msg_pool[TOS_CFG_MSG_POOL_SIZE]
的成員變量進行初始化,初始化對應的列表節點,而且將它掛載到空閒消息列表上k_msg_freelist
初始化完成示意圖:(假設只有3個消息)
學習
__KERNEL__ void msgpool_init(void) { uint32_t i; for (i = 0; i < TOS_CFG_MSG_POOL_SIZE; ++i) { tos_list_init(&k_msg_pool[i].list); tos_list_add(&k_msg_pool[i].list, &k_msg_freelist); } }
__API__ k_err_t tos_knl_init(void) { ··· #if (TOS_CFG_MSG_EN) > 0 msgpool_init(); #endif ··· }
這個函數在隊列建立中會被調用,固然他也能夠本身做爲用戶API接口提供給用戶使用,而非僅僅是內核API接口。
這個函數的本質上就是初始化消息隊列中的消息列表queue_head
。
初始化完成示意圖:
測試
__API__ k_err_t tos_msg_queue_create(k_msg_queue_t *msg_queue) { TOS_PTR_SANITY_CHECK(msg_queue); #if TOS_CFG_OBJECT_VERIFY_EN > 0u knl_object_init(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE); #endif tos_list_init(&msg_queue->queue_head); return K_ERR_NONE; }
tos_msg_queue_destroy()
函數用於銷燬一個消息隊列,當消息隊列不在使用是能夠將其銷燬,銷燬的本質實際上是將消息隊列控制塊的內容進行清除,首先判斷一下消息隊列控制塊的類型是KNL_OBJ_TYPE_MSG_QUEUE
,這個函數只能銷燬隊列類型的控制塊。而後調用tos_msg_queue_flush()
函數將隊列的消息列表的消息所有「清空
」,「清空」的意思是將掛載到隊列上的消息釋放回消息池(若是消息隊列的消息列表存在消息,使用msgpool_free()
函數釋放消息)。而且經過tos_list_init()
函數將消息隊列的消息列表進行初始化,knl_object_deinit()
函數是爲了確保消息隊列已經被銷燬,此時消息隊列控制塊的pend_obj
成員變量中的type
屬性標識爲KNL_OBJ_TYPE_NONE
。ui
可是有一點要注意,由於隊列控制塊的RAM是由編譯器靜態分配的,因此即便是銷燬了隊列,這個內存也是沒辦法釋放的~.net
__API__ k_err_t tos_msg_queue_destroy(k_msg_queue_t *msg_queue) { TOS_PTR_SANITY_CHECK(msg_queue); #if TOS_CFG_OBJECT_VERIFY_EN > 0u if (!knl_object_verify(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE)) { return K_ERR_OBJ_INVALID; } #endif tos_msg_queue_flush(msg_queue); tos_list_init(&msg_queue->queue_head); #if TOS_CFG_OBJECT_VERIFY_EN > 0u knl_object_deinit(&msg_queue->knl_obj); #endif return K_ERR_NONE; }
__API__ void tos_msg_queue_flush(k_msg_queue_t *msg_queue) { TOS_CPU_CPSR_ALLOC(); k_list_t *curr, *next; TOS_CPU_INT_DISABLE(); TOS_LIST_FOR_EACH_SAFE(curr, next, &msg_queue->queue_head) { msgpool_free(TOS_LIST_ENTRY(curr, k_msg_t, list)); } TOS_CPU_INT_ENABLE(); }
tos_msg_queue_get()
函數用於從消息隊列中獲取消息,獲取到的消息經過msg_addr
參數返回,獲取到消息的大小經過msg_size
參數返回給用戶,當獲取成功是返回K_ERR_NONE
,不然返回對應的錯誤代碼。
這個從消息隊列中獲取消息的函數是不會產生阻塞的,若是有消息則獲取成功,不然就獲取失敗,它的實現過程以下:
TOS_CFG_OBJECT_VERIFY_EN
宏定義使能了,就調用knl_object_verify()
函數確保是從消息隊列中獲取消息,而後經過TOS_LIST_FIRST_ENTRY_OR_NULL
判斷一下是消息隊列的消息列表否存在消息,若是不存在則返回K_ERR_MSG_QUEUE_EMPTY
表示消息隊列是空的,反正將獲取成功,獲取成功後須要使用msgpool_free()
函數將消息釋放回消息池。code
__API__ k_err_t tos_msg_queue_get(k_msg_queue_t *msg_queue, void **msg_addr, size_t *msg_size) { TOS_CPU_CPSR_ALLOC(); k_msg_t *msg; #if TOS_CFG_OBJECT_VERIFY_EN > 0u if (!knl_object_verify(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE)) { return K_ERR_OBJ_INVALID; } #endif TOS_CPU_INT_DISABLE(); msg = TOS_LIST_FIRST_ENTRY_OR_NULL(&msg_queue->queue_head, k_msg_t, list); if (!msg) { TOS_CPU_INT_ENABLE(); return K_ERR_MSG_QUEUE_EMPTY; } *msg_addr = msg->msg_addr; *msg_size = msg->msg_size; msgpool_free(msg); TOS_CPU_INT_ENABLE(); return K_ERR_NONE; }
當發送消息時,TencentOS tiny
會從消息池(空閒消息列表)中取出一個空閒消息,掛載到消息隊列的消息列表中,能夠經過opt
參數選擇掛載到消息列表的末尾或者是頭部,所以消息隊列的寫入是支持FIFO
與LIFO
方式的,msg_queue
是要寫入消息的消息隊列控制塊,msg_addr
、msg_size
則是要寫入消息的地址與大小。blog
寫入消息的過程很是簡單,直接經過msgpool_alloc()
函數從消息池取出一個空閒消息,若是系統不存在空閒的消息,則直接返回錯誤代碼K_ERR_MSG_QUEUE_FULL
表示系統可用的消息已經被使用完。若是取出空閒消息成功則將要寫入的消息地址與大小記錄到消息池的msg_addr
與 msg_size
成員變量中,而後經過opt
參數選擇將消息掛載到消息列表的位置(頭部或者是尾部)。接口
__API__ k_err_t tos_msg_queue_put(k_msg_queue_t *msg_queue, void *msg_addr, size_t msg_size, k_opt_t opt) { TOS_CPU_CPSR_ALLOC(); k_msg_t *msg; #if TOS_CFG_OBJECT_VERIFY_EN > 0u if (!knl_object_verify(&msg_queue->knl_obj, KNL_OBJ_TYPE_MSG_QUEUE)) { return K_ERR_OBJ_INVALID; } #endif TOS_CPU_INT_DISABLE(); msg = msgpool_alloc(); if (!msg) { TOS_CPU_INT_ENABLE(); return K_ERR_MSG_QUEUE_FULL; } msg->msg_addr = msg_addr; msg->msg_size = msg_size; if (opt & TOS_OPT_MSG_PUT_LIFO) { tos_list_add(&msg->list, &msg_queue->queue_head); } else { tos_list_add_tail(&msg->list, &msg_queue->queue_head); } TOS_CPU_INT_ENABLE(); return K_ERR_NONE; }
#include "stm32f10x.h" #include "bsp_usart.h" #include "tos.h" k_msg_queue_t test_msg_queue_00; k_task_t task1; k_task_t task2; k_stack_t task_stack1[1024]; k_stack_t task_stack2[1024]; void test_task1(void *Parameter) { k_err_t err; int i = 0; int msg_received; size_t msg_size = 0; while(1) { printf("queue pend\r\n"); for (i = 0; i < 3; ++i) { err = tos_msg_queue_get(&test_msg_queue_00, (void **)&msg_received, &msg_size); if (err == K_ERR_NONE) printf("msg queue get is %d \r\n",msg_received); if (err == K_ERR_PEND_DESTROY) { printf("queue is destroy\r\n"); tos_task_delay(TOS_TIME_FOREVER - 1); } } tos_task_delay(1000); } } void test_task2(void *Parameter) { k_err_t err; int i = 0; uint32_t msgs[3] = { 1, 2, 3 }; printf("task2 running\r\n"); while(1) { for (i = 0; i < 3; ++i) { err = tos_msg_queue_put(&test_msg_queue_00, (void *)(msgs[i]), sizeof(uint32_t), TOS_OPT_MSG_PUT_FIFO); if (err != K_ERR_NONE) printf("msg queue put fail! code : %d \r\n",err); } tos_task_delay(1000); } } /** * @brief 主函數 * @param 無 * @retval 無 */ int main(void) { k_err_t err; /*初始化USART 配置模式爲 115200 8-N-1,中斷接收*/ USART_Config(); printf("Welcome to TencentOS tiny\r\n"); tos_knl_init(); // TOS Tiny kernel initialize tos_robin_config(TOS_ROBIN_STATE_ENABLED, (k_timeslice_t)500u); printf("create test_queue_00\r\n"); err = tos_msg_queue_create(&test_msg_queue_00); if(err != K_ERR_NONE) printf("TencentOS Create test_msg_queue_00 fail! code : %d \r\n",err); printf("create task1\r\n"); err = tos_task_create(&task1, "task1", test_task1, NULL, 3, task_stack1, 1024, 20); if(err != K_ERR_NONE) printf("TencentOS Create task1 fail! code : %d \r\n",err); printf("create task2\r\n"); err = tos_task_create(&task2, "task2", test_task2, NULL, 4, task_stack2, 1024, 20); if(err != K_ERR_NONE) printf("TencentOS Create task2 fail! code : %d \r\n",err); tos_knl_start(); // Start TOS Tiny }
相關代碼能夠在公衆號後臺回覆 「 19 」 獲取。
更多資料歡迎關注「物聯網IoT開發」公衆號!隊列