RT-Thread移植到S5P4418(五):線程通信

RT-Thread實現了三種線程間通信方式,郵箱(mailbox)、消息隊列(messagequeue)、和信號(signal)。

郵箱

郵箱中的每一封郵件只能容納固定的 4 字節內容。任何線程都可以發送或接受郵件消息。

郵箱控制塊
struct rt_mailbox
{
    struct rt_ipc_object parent;

    rt_uint32_t* msg_pool;                 /* 郵箱緩衝區的開始地址 */
    rt_uint16_t size;                       /* 郵箱緩衝區的大小     */

    rt_uint16_t entry;                       /* 郵箱中郵件的數目     */
    rt_uint16_t in_offset, out_offset;    /* 郵箱緩衝的進出指針   */
    rt_list_t suspend_sender_thread;      /* 發送線程的掛起等待隊列 */
};
typedef struct rt_mailbox* rt_mailbox_t;

在這裏插入圖片描述

創建郵箱

rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)

  • .msg_pool 就是一個循環隊列,FIFO,通過.in_offset, .out_offset記錄進出位置下標。
  • .suspend_sender_thread 當郵箱滿時,發送線程會掛在該鏈上。
發送郵件

rt_err_t rt_mb_send_wait (rt_mailbox_t mb, rt_uint32_t value)
當發送郵件時, 當且僅當郵箱還沒滿郵件的時候才能進行發送,如果郵箱已滿,可以根據用戶設定的等待時間進行等待,當郵箱中的郵件被收取而空出空間來時,等待掛起的發送線程將被喚醒繼續發送的過程,當等待時間到了還未完成發送郵件,或者未設置等待時間,此時發送郵件失敗,發送郵件的線程或者中斷程序會收到一個錯誤碼( -RT_EFULL)。如果郵件發送成功,會喚起等待接收郵件的線程。線程發送郵件可以帶阻塞,但在中斷中不能採用任何帶阻塞的方式發送郵件。

接受郵件

rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout)
接收郵件時, 根據郵箱控制塊中的 entry 判斷隊列是否有郵件,如果郵箱的郵件非空,那麼可以根據 out_offset 找到最先發送到郵箱中的郵件進行接收。在接收時如果郵箱爲空,如果用戶設置了等待超時時間,系統會將當前線程掛起,當達到設置的超時時間,郵箱依然未收到郵件時,那麼線程將被喚醒並返回-RT_ETIMEOUT。如果郵箱中存在郵件,那麼接收線程將複製郵箱中的 4 個字節郵件到接收線程中。如果接收成功,會喚起等待發送的線程。

工程文件
  • rtt2a9_mailbox
    線程2發送郵件,線程1等待按鍵事件後接收郵件消息。

消息隊列

消息隊列是另一種常用的線程間通訊方式,是郵箱的擴展。可以應用在多種場合:線程間的消息交換、使用串口接收不定長數據等。

  • 相比於郵箱,消息隊列能發送不固定長度的消息,但沒有發送線程的掛起列表。
  • 支持發送緊急消息,緊急消息會直接加到對頭。
消息隊列控制塊
struct rt_messagequeue
{
    struct rt_ipc_object parent;

    void* msg_pool;            /* 指向存放消息的緩衝區的指針 */

    rt_uint16_t msg_size;    /* 每個消息的長度 */
    rt_uint16_t max_msgs;    /* 最大能夠容納的消息數 */

    rt_uint16_t entry;        /* 隊列中已有的消息數 */

    void* msg_queue_head;    /* 消息鏈表頭 */
    void* msg_queue_tail;    /* 消息鏈表尾 */
    void* msg_queue_free;    /* 空閒消息鏈表 */
};
typedef struct rt_messagequeue* rt_mq_t;

在這裏插入圖片描述

創建消息隊列
rt_mq_t rt_mq_create(const char *name,
                     rt_size_t   msg_size,
                     rt_size_t   max_msgs,
                     rt_uint8_t  flag);

.msg_size要注意對齊。創建後,整個鏈表都是空閒塊,.msg_queue_free指向表頭,.msg_queue_head.msg_queue_tail指向RT_NULL

發送消息

rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size);
從空閒鏈表中摘下.msg_queue_free指向的空閒塊,加到消息隊列的尾部。如果消息隊列爲空,則更新.msg_queue_head.msg_queue_tail。發送緊急消息時,把消息直接加到對頭。

接收消息
rt_err_t rt_mq_recv(rt_mq_t    mq,
                    void      *buffer,
                    rt_size_t  size,
                    rt_int32_t timeout);

通過.entry判斷隊列中是否有消息,沒有,則掛起或直接返回。有,則從對頭取出消息,拷貝數據後將內存塊掛到空閒隊列。
在這裏插入圖片描述

工程文件

線程2發送消息,消息中帶一個信號量,用於線程1接收到消息後通知線程2繼續發送消息。


信號

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章