1. 事件集的使用
單個指定事件喚醒線程,任意事件喚醒線程,多個指定事件一起喚醒線程。信號量主要用於“一對一”的線程同步,當需要“一對多”、“多對一”、“多對多”的同步時,就需要事件集來處理了。RT-Thread中的事件集用一個32位無符號整型變量來表示,變量中的一個位代表一個事件,線程通過“邏輯與”或“邏輯或”與一個或多個事件建立關聯形成一個事件組合。
- 事件的“邏輯或”也稱為是獨立型同步,指的是線程與任何事件之一發生同步,只要有一個事件發生,即滿足條件
- 事件的“邏輯與”,也稱為是關聯型同步,指的是線程與若干事件都發生同步,只有這些事件全部發生,才滿足條件
1.1 事件集控制塊
struct rt_event
{
struct rt_ipc_object parent; // 從ipc_object繼承而來
rt_uint32_t set; // 事件集 set
}
typedef struct rt_event *rt_event_t;
靜態事件集:struct rt_event static_evt;
動態事件集:rt_event_t dynamic_evt;
1.2 事件集操作
- 初始化與脫離
靜態事件集操作
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t falg) //上同
rt_err_t rt_event_detach(rt_event_t event)
- 創建與刪除
動態事件集操作
rt_event_t rt_event_create(const char *name, rt_uint8_t flag)
rt_err_t rt_event_delete(rt_event event)
- 發送事件
// set的值為0x01則代表第0個事件發生了,0x08則代表第3個事件發生了
// 1 << 0, 1 << 3
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set)
- 接受事件
rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t_t set, rt_uint8_t option, rt_int32_t timeout, rt_uint32_t *recved)
// set的值表示對哪個事件感興趣。例如0x01 | 0x08,則表示對第0個事件和第3個事件感興趣
// option:
RT_EVENT_FLAG_AND:都發生才喚醒
RT_EVENT_FLAG_OR:有一個發生就喚醒
RT_EVENT_FLAG_CLEAR:線程喚醒后,系統會將事件的set對應的位清零。否則系統不會清除對應位
// timeout: 上同
// recved: 保存接收到的事件set
2. 郵箱的使用
郵箱用於線程間通信,特點是開銷比較低,效率較高。郵箱中的每一封郵件只能容納固定的4字節內容(針對32位處理系統,指針的大小即為4個字節,所以一封郵件恰好能夠容納一個指針)。線程或中斷服務例程把一封4字節長度的郵件發送到郵箱中,而其他需要的線程可以從郵箱中接受這些郵件並進行處理
2.1 郵箱控制塊
struct rt_mailbox
{
struct rt_ipc_object parent; //從IPC對象繼承而來
rt_uint32_t *msg_pool; //指向郵箱消息的緩沖區的地址
rt_uint16_t size; //郵箱的容量,可以放多少個郵件
rt_uint16_t entry; // 郵箱中郵件的數目
rt_uint16_t in_offset; // 郵箱進偏移量
rt_uint16_t out_offset; // 郵箱出偏移量
rt_list_t suspend_sender_thread; // 記錄了掛起在該郵箱的線程。比如郵箱滿了,這些線程就不能發了,要掛起等待
}
typedef struct rt_mailbox *rt_mailbox_t;
靜態郵箱:struct rt_mailbox static_mb;
動態郵箱:rt_mailbox_t dynamic_mb;
2.2 郵箱的操作
- 初始化與脫離
// 靜態郵箱。flag: RT_IPC_FLAG_FIFO, RT_IPC_FLAG_PRIO
// 如果size=10,則 msgpool就要有4 * 10 = 40byte的空間
rt_err_t rt_mb_init(rt_mailbox_t mb, const char *name, void *msgpool, rt_size_t size, rt_uint8_t flag)
rt_err_t rt_mb_detach(rt_mailbox_t mb)
- 創建與刪除
// 動態郵箱
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag)
rt_err_t rt_mbdelete(rt_mailbox_t mb)
- 發送郵件
// value就是郵件的內容,4字節。如果發送的內容<4字節,則直接賦值給value即可,如果很多,那么可以傳送地址。
// 如果郵箱已經滿了,那么會直接返回錯誤
// 可以在線程和中斷中調用
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value);
// 如果郵箱滿了,則最多等待timeout時間。
// 只能在線程中調用,因為它會造成阻塞
rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_uint32_t value, rt_int32_t timeout)
- 接收郵件
rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout)
3. 消息隊列
消息隊列是RT-Thread另一種常用的線程間通信方式,消息隊列是對郵箱的擴展。消息隊列能夠接收來自線程或中斷服務例程中發出的不固定長度的消息,並把消息緩存在自己的內存空間中,而其它線程能夠從消息隊列中讀取相應的消息,並進行相應的處理。支持緊急消息發送,即將緊急消息鏈接到消息列表鏈表頭。當消息隊列滿,還往消息隊列發送消息,該發送就會失敗。當消息隊列空,還從消息隊列接收消息,該接收就會失敗。
3.1 消息隊列控制塊
struct rt_messagequeue
{
struct rt_ipc_object parent; // 繼承自IPC對象
void *msg_pool; // 指向消息隊列空間的地址
rt_uint16_t msg_size; // 消息最大長度.在rtconfig.h 中定義對其字節,一般是4字節對齊。因此最小為4,應設置為4的倍數
rt_uint16_t max_msgs; // 消息隊列容量,能容納最多消息個數。例如設置msg_pool的大小為1024byte,那么max_msgs = 1024 / (msg_size + 4指針地址) = 1024/8
rt_uint16_t entry; // 消息隊列中消息個數
void *msg_queue_head; // 消息隊列頭指針
void *msg_queue_tail; // 消息隊列尾指針
void *msg_queue_free; // 消息隊列未被使用的消息框
}
typedef struct rt_messagequeue *rt_mq_t;
靜態消息隊列:struct rt_messagequeue static_mq
動態消息隊列:rt_mq_t dynamic_mq
3.2 消息隊列的操作
- 初始化與脫離
rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, rt_size_t msg_size, rt_size_t pool_size, rt_uint8_t flag) // RT_IPC_FLAG_FIFO, RT_IPC_FLAG_PRIO
rt_err_t rt_mq_detach(rt_mq_t mq)
- 創建於刪除
rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag)
rt_err_t rt_mq_delete(rt_mq_t mq)
- 發送消息
// size <= msg_size.將消息放在尾部
rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size)
// 緊急消息,消息放在列表頭部
rt_err_t rt_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size)
- 接收消息
// timeout不等於0,則收不到消息就會掛起
rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, rt_size_t size, rt_int32_t timeout)
4. 軟件定時器
軟件定時器是由操作系統提供的一類系統接口,構建在硬件定時器基礎之上(系統滴答定時器),軟件定時器使系統能夠提供不受數目限制的定時器服務。RT-Thread提供的軟件定時器,以系統節拍(OS Tick)的時間長度為定時單位,提供了基於系統節拍整數倍的定時能力,即定時器數值是OS Tick的整數倍。例如一個OS Tick是10ms,那么上層軟件定時器只能提供10的倍數的定時。到時之后,會調用用戶設置的回調函數
4.1 定時器模式
4.1.1 HARDTIMER模式
超時函數在中斷上下文環境中執行。此模式在定時器初始化時指定。對於超時函數的要求和中斷服務例程的要求相同。執行時間應該盡量短,執行時不應導致當前上下文掛起,HARD_TIMER模式是RT-Thread軟件定時器的默認方式
4.1.2 SOFTTIMER模式
超時函數在系統timer線程的線程上下文中執行。通過宏定義RT_USING_TIMER_SOFT來決定是否啟用該模式。當啟動SOFTTIMER模式后,可以在定時器初始化時指定定時器工作在SOFTTIMER模式
4.2 軟件定時器控制塊
struct rt_timer
{
struct rt_object parent; // 從系統對象繼承而來
rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL]; // 系統定時器鏈表
void (*timeout_func)(void *parameter); // 超時回調函數
void *parameter; // 回調函數輸入參數
rt_tick_t init_tick; // 指定超時的時鍾節拍。例如60 * 10 = 600ms
rt_tick_t timeout_tick; // 系統在超時時的系統節拍
};
typedef struct rt_timer *rt_timer_t;
靜態軟件定時器:struct rt_timer static_timer
動態軟件定時器:rt_timer_t dynamic_timer
4.3 軟件定時器的操作
- 初始化與脫離
void rt_timer_init(rt_timer_t timer, const char *name, void(*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag);
// RT_TIMER_FLAG_ONE_SHOT, RT_TIMER_FLAG_PERIODIC, RT_TIMER_FLAG_HARD_TIMER, RT_TIMER_FLAG_SOFT_TIMER
第1和第2個選一個 | 第3和第4個選一個
// 沒有回調參數就傳RT_NULL
rt_err_t rt_timer_detach(rt_timer_t timer)
- 創建與刪除
rt_timer_t rt_timer_create(const char *name, void(*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag)
rt_err_t rt_timer_delete(rt_timer_t timer)
- 啟動定時器
rt_err_t rt_timer_start(rt_timer_t timer)
- 停止定時器
rt_err_t rt_timer_stop(rt_timer_t timer)
5. 內存池
動態內存堆可以分配任意大小的內存塊,非常靈活和方便,但其存在明顯的缺點:一是分配效率不高,在每次分配時,都要進行空閑內存塊查找;二是容易產生內存碎片。為了提高內存分配效率,並且避免內存碎片,RT-Thread提供了另一種內存管理方法:內存池。內存池是一種內存分配方式,用於分配大量大小相同的小內存塊,使用內存池可以極大的加快內存分配與釋放的速度,且能盡量避免內存碎片化。RT-Thread的內存池支持線程掛起,當內存池無空閑內存塊時,申請線程會被掛起,直到內存池中有新的可用內存塊,再將掛起的線程喚醒。基於這個特點內存池非常適合需要通過內存資源進行同步的場景。
5.1 內存池控制塊
struct rt_mempool
{
struct rt_object parent; // 從系統對象繼承
void *start_address; // 內存池起始地址
rt_size_t size; // 內存池大小
rt_size_t block_size; // 內存池中一個內存塊大小
rt_uint8_t *block_list; // 內存池中小內存塊鏈表
rt_size_t block_total_count; // 記錄內存池中能容納多少小內存塊
rt_size_t block_free_count; // 內存池中空閑內存塊個數
rt_list_t suspend_thread; // 掛載在該資源上的線程
rt_size_t suspend_thread_count; // 掛載在該資源上的線程個數
};
typedef struct rt_mempool *rt_mp_t;
靜態內存池:struct rt_mempool static_mp;
動態內存池:rt_mp_t dynamic_mp;
5.2 內存池操作
- 初始化與脫離
靜態內存池
// block_size仍然要遵循字節對齊。例如4字節對齊,就必須設置成4的整數倍。有了block_size和size就可以計算內存塊數量=size/(block_size + 4)。其中4為指針大小
rt_err_t rt_mp_init(struct rt_mempool *mp, const char *name, void *start, rt_size_t size, rt_size_t block_size)
rt_err_t rt_mp_detach(struct rt_mempool *mp)
- 創建與刪除
rt_mp_t rt_mp_create(const char *name, rt_size_t block_count, rt_size_t block_size)
rt_err_t rt_mp_delete(rt_mp_t mp)
- 申請內存塊
// time參數為0,則立刻返回結果,如果time大於0,則無內存塊會掛起線程,如果time小於0,則無內存塊會一直掛起線程,直到有內存塊
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
- 釋放內存塊
// 輸入內存塊的地址
void rt_mp_free(void *block)
參考文獻
本文作者: CrazyCatJack
本文鏈接: https://www.cnblogs.com/CrazyCatJack/p/14408849.html
版權聲明:本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協議。轉載請注明出處!
關注博主:如果您覺得該文章對您有幫助,可以點擊文章右下角推薦一下,您的支持將成為我最大的動力!