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

来源:https://www.cnblogs.com/iot-dev/archive/2019/10/16/11688931.html
-Advertisement-
Play Games

消息隊列 在前一篇文章中 "【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參數選擇掛載到消息列表的末尾或者是頭部,因此消息隊列的寫入是支持FIFOLIFO方式的,msg_queue是要寫入消息的消息隊列控制塊,msg_addrmsg_size則是要寫入消息的地址與大小。

寫入消息的過程非常簡單,直接通過msgpool_alloc()函數從消息池取出一個空閑消息,如果系統不存在空閑的消息,則直接返回錯誤代碼K_ERR_MSG_QUEUE_FULL表示系統可用的消息已經被使用完。如果取出空閑消息成功則將要寫入的消息地址與大小記錄到消息池的msg_addrmsg_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開發”公眾號!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 場景 C#中委托與事件的使用-以Winform中跨窗體傳值為例: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/100150700 參考上面的博客。 需求是在圖形選項窗體中刷新主窗體的圖。 註: 博客主頁: https://blo ...
  • ManualResetEvent表示線程同步事件,可以對所有進行等待的線程進行統一管理(收到信號時必須手動重置該事件) 其構造函數為: 參數 initialState 表示是否初始化,如果為 true,則將初始狀態設置為終止(不阻塞);如果為 false,則將初始狀態設置為非終止(阻塞)。 註意:如 ...
  • 前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
  • 本地所有文件: git rm --cached readme1.txt 刪除readme1.txt的跟蹤,並保留在本地。 git rm --f readme1.txt 刪除readme1.txt的跟蹤,並且刪除本地文件。 文件夾: 如果是對所有文件都取消跟蹤的話,就是 git rm -r --cac ...
  • 軟體定時器的基本概念 TencentOS tiny 的軟體定時器是由操作系統提供的一類 ,它構建在硬體定時器基礎之上,使系統能夠提供不受硬體定時器資源限制的定時器服務,本質上軟體定時器的使用相當於擴展了定時器的數量,允許創建更多的定時業務,它實現的功能與硬體定時器也是類似的。 硬體定時器是晶元本身提 ...
  • 引言 大家在裸機編程中很可能經常用到 這種變數,用來標誌一下某個事件的發生,然後在迴圈中判斷這些標誌是否發生,如果是等待多個事件的話,還可能會 這樣子做判斷。當然,如果聰明一點的同學就會拿 的`某些位 A`事件,第二位表示 事件,當這兩個事件都發生的時候,就判斷 的值是多少,從而判斷出哪個事件發生了 ...
  • 互斥鎖 互斥鎖又稱互斥互斥鎖,是一種特殊的信號量,它和信號量不同的是,它具有 等特性,在操作系統中常用於對臨界資源的 處理。在任意時刻互斥鎖的狀態只有兩種, ,當互斥鎖被任務持有時,該互斥鎖處於閉鎖狀態,當該任務釋放互斥鎖時,該互斥鎖處於開鎖狀態。 一個任務持有互斥鎖就表示它擁有互斥鎖的所有權,只有 ...
  • 信號量 信號量( )在操作系統中是一種實現系統中任務與任務、任務與中斷間同步或者臨界資源互斥保護的機制。在多任務系統中,各任務之間常需要同步或互斥,信號量就可以為用戶提供這方面的支持。 抽象來說,信號量是一個非負整數,每當信號量被獲取( )時,該整數會減一,當該整數的值為 時,表示信號量處於無效狀態 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...