消息隊列 在前一篇文章中 "【TencentOS tiny學習】源碼分析(3)——隊列" 我們描述了TencentOS tiny的隊列實現,同時也點出了TencentOS tiny的隊列是依賴於消息隊列的,那麼我們今天來看看消息隊列的實現。 其實消息隊列是TencentOS tiny的一個 基礎組件 ...
消息隊列
在前一篇文章中【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
。
但是有一點要註意,因為隊列控制塊的RAM是由編譯器靜態分配的,所以即使是銷毀了隊列,這個記憶體也是沒辦法釋放的~
__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()
函數將消息釋放回消息池。
__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
則是要寫入消息的地址與大小。
寫入消息的過程非常簡單,直接通過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開發”公眾號!