單個指定事件喚醒線程,任意事件喚醒線程,多個指定事件一塊兒喚醒線程。信號量主要用於「一對一」的線程同步,當須要「一對多」、「多對一」、「多對多」的同步時,就須要事件集來處理了。RT-Thread中的事件集用一個32位無符號整型變量來表示,變量中的一個位表明一個事件,線程經過「邏輯與」或「邏輯或」與一個或多個事件創建關聯造成一個事件組合。html
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;
靜態事件集操做 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
郵箱用於線程間通訊,特色是開銷比較低,效率較高。郵箱中的每一封郵件只能容納固定的4字節內容(針對32位處理系統,指針的大小即爲4個字節,因此一封郵件剛好可以容納一個指針)。線程或中斷服務例程把一封4字節長度的郵件發送到郵箱中,而其餘須要的線程能夠從郵箱中接受這些郵件並進行處理
緩存
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;
// 靜態郵箱。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)
消息隊列是RT-Thread另外一種經常使用的線程間通訊方式,消息隊列是對郵箱的擴展。消息隊列可以接收來自線程或中斷服務例程中發出的不固定長度的消息,並把消息緩存在本身的內存空間中,而其它線程可以從消息隊列中讀取相應的消息,並進行相應的處理。支持緊急消息發送,即將緊急消息連接到消息列表鏈表頭。當消息隊列滿,還往消息隊列發送消息,該發送就會失敗。當消息隊列空,還從消息隊列接收消息,該接收就會失敗。
ide
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
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)
軟件定時器是由操做系統提供的一類系統接口,構建在硬件定時器基礎之上(系統滴答定時器),軟件定時器使系統可以提供不受數目限制的定時器服務。RT-Thread提供的軟件定時器,以系統節拍(OS Tick)的時間長度爲定時單位,提供了基於系統節拍整數倍的定時能力,即定時器數值是OS Tick的整數倍。例如一個OS Tick是10ms,那麼上層軟件定時器只能提供10的倍數的定時。到時以後,會調用用戶設置的回調函數函數
超時函數在中斷上下文環境中執行。此模式在定時器初始化時指定。對於超時函數的要求和中斷服務例程的要求相同。執行時間應該儘可能短,執行時不該致使當前上下文掛起,HARD_TIMER模式是RT-Thread軟件定時器的默認方式ui
超時函數在系統timer線程的線程上下文中執行。經過宏定義RT_USING_TIMER_SOFT來決定是否啓用該模式。當啓動SOFTTIMER模式後,能夠在定時器初始化時指定定時器工做在SOFTTIMER模式操作系統
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
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)
動態內存堆能夠分配任意大小的內存塊,很是靈活和方便,但其存在明顯的缺點:一是分配效率不高,在每次分配時,都要進行空閒內存塊查找;二是容易產生內存碎片。爲了提升內存分配效率,而且避免內存碎片,RT-Thread提供了另外一種內存管理方法:內存池。內存池是一種內存分配方式,用於分配大量大小相同的小內存塊,使用內存池能夠極大的加快內存分配與釋放的速度,且能儘可能避免內存碎片化。RT-Thread的內存池支持線程掛起,當內存池無空閒內存塊時,申請線程會被掛起,直到內存池中有新的可用內存塊,再將掛起的線程喚醒。基於這個特色內存池很是適合須要經過內存資源進行同步的場景。
線程
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;
靜態內存池 // 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.htmlcode
版權聲明:本博客全部文章除特別聲明外,均採用 BY-NC-SA 許可協議。轉載請註明出處!視頻
關注博主:若是您以爲該文章對您有幫助,能夠點擊文章右下角推薦一下,您的支持將成爲我最大的動力!