【TencentOS tiny】深度源碼分析(4)——消息隊列

消息隊列

在前一篇文章中【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()函數就是用來初始化消息池的,該函數的定義位於 tosmsg.c文件中,函數的實現主要是經過一個for循環,將消息池`kmsgpool[TOSCFGMSGPOOLSIZE]的成員變量進行初始化,初始化對應的列表節點,而且將它掛載到空閒消息列表上kmsg_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_NONEui

可是有一點要注意,由於隊列控制塊的RAM是由編譯器靜態分配的,因此即便是銷燬了隊列,這個內存也是沒辦法釋放的~spa

__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()函數將消息釋放回消息池。.net

__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參數選擇掛載到消息列表的末尾或者是頭部,所以消息隊列的寫入是支持FIFOLIFO方式的,msg_queue是要寫入消息的消息隊列控制塊,msg_addrmsg_size則是要寫入消息的地址與大小。code

寫入消息的過程很是簡單,直接經過msgpool_alloc()函數從消息池取出一個空閒消息,若是系統不存在空閒的消息,則直接返回錯誤代碼K_ERR_MSG_QUEUE_FULL表示系統可用的消息已經被使用完。若是取出空閒消息成功則將要寫入的消息地址與大小記錄到消息池的msg_addrmsg_size 成員變量中,而後經過opt參數選擇將消息掛載到消息列表的位置(頭部或者是尾部)。cdn

__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開發」公衆號blog

相關文章
相關標籤/搜索