如何改善FreeRTOS運(yùn)行的速度、RAM大小
作者 | strongerHuang
微信公眾號(hào) | 嵌入式專(zhuān)欄
之前分享了《 FreeRTOS V10.4.0更新了哪些功能? 》,今天就來(lái)詳細(xì)講述其中的一個(gè)知識(shí)點(diǎn):FreeRTOS的直接任務(wù)(消息)通知,這樣做的目的就是減少RAM占用空間并加快執(zhí)行速度。
1
寫(xiě)在前面 幾乎所有RTOS操作系統(tǒng)都提供了隊(duì)列和信號(hào)量的功能,對(duì)于大部分新手來(lái)說(shuō),使用隊(duì)列和信號(hào)量是必備技能。
但是,在大多數(shù)情況下,他們都是使用“中介對(duì)象”進(jìn)行通信,而并非“直接任務(wù)消息”通信。
通過(guò)“中介對(duì)象”進(jìn)行通信,每一組隊(duì)列或信號(hào)量都會(huì)分配一段內(nèi)存(消息緩沖區(qū)和流緩沖區(qū))。就存在一個(gè)問(wèn)題,如果隊(duì)列或信號(hào)量比較多,勢(shì)必造成更大的內(nèi)存開(kāi)支。
但是,如果通過(guò)本文說(shuō)的“直接消息”通信,會(huì)節(jié)約很多內(nèi)存。
2
什么是直接任務(wù)通知? 大多數(shù)任務(wù)間通信方法都通過(guò) 中介對(duì)象 ,例如隊(duì)列,信號(hào)量或事件組。 發(fā)送任務(wù)寫(xiě)入通信對(duì)象,接收任務(wù)從通信對(duì)象讀取。
比如FreeRTOS的隊(duì)列通信,首先創(chuàng)建隊(duì)列之前要定義一個(gè)隊(duì)列:
QueueHandle_t xQueue; xQueue = xQueueCreate(10, sizeof( /* 長(zhǎng)度 */ ) ); 而這個(gè)隊(duì)列包含了很多中介對(duì)象:
大家可以算一下這個(gè)“中介對(duì)象”會(huì)占用多少RAM空間?
通過(guò)一個(gè)代碼示意圖理解中介對(duì)象通信:
直接任務(wù)通知: 當(dāng)使用直接任務(wù)通知時(shí),顧名思義,發(fā)送任務(wù)將通知直接發(fā)送給接收任務(wù),而無(wú)需中介對(duì)象。
通過(guò)一個(gè)代碼示意圖理解:
從FreeRTOS V10.4.0開(kāi)始,每個(gè)任務(wù)都有一系列通知。每個(gè)通知都包含一個(gè)32位值和一個(gè)布爾狀態(tài),它們一起僅消耗5個(gè)字節(jié)的RAM。
就像任務(wù)可以阻止二進(jìn)制信號(hào)量等待該信號(hào)量變?yōu)椤翱捎谩币粯?,任?wù)可以阻止通知以等待該通知的狀態(tài)變?yōu)椤按幚怼薄M瑯?,就像任?wù)可以阻止計(jì)數(shù)信號(hào)量以等待該信號(hào)量的計(jì)數(shù)變?yōu)榉橇阋粯樱蝿?wù)可以阻止通知以等待該通知的值變?yōu)榉橇?。下面的第一個(gè)示例演示了這種情況。
通知不僅可以傳達(dá)事件,還可以通過(guò)多種方式傳達(dá)數(shù)據(jù)。
3
進(jìn)一步分析直接任務(wù)通知 通過(guò)對(duì)比 FreeRTOS V10.4.0 和之前版本,你會(huì)發(fā)現(xiàn) V10.4.0 多了一些API,比如ulTaskNotifyTake / ulTaskNotifyTakeIndexed:
在官網(wǎng)也有針對(duì)這些API的詳細(xì)介紹和說(shuō)明,以及應(yīng)用代碼例子:
直接任務(wù)通信API說(shuō)明地址:
https://www.freertos.org/RTOS-task-notification-API.html
4
使用直接任務(wù)通知性能優(yōu)勢(shì)和使用限制 任務(wù)通知的靈活性使它們可以在需要?jiǎng)?chuàng)建單獨(dú)的隊(duì)列、 二進(jìn)制信號(hào)量、 數(shù)信號(hào)量或事件組的情況下使用。
與使用中介對(duì)象(例如信號(hào)量)來(lái)取消阻止任務(wù)相比,使用直接通知取消阻止RTOS任務(wù)的速度快了45% (來(lái)自官方數(shù)據(jù)) ,并且使用的RAM更少。
當(dāng)然,有這些性能優(yōu)勢(shì),也肯定一些限制:
-
僅當(dāng)只有一個(gè)任務(wù)可以作為事件的接收者時(shí),才可以使用RTOS任務(wù)通知。但是,在大多數(shù)實(shí)際使用情況下都可以滿足此條件,例如中斷使執(zhí)行任務(wù)處理的任務(wù)中斷時(shí),該任務(wù)將處理該中斷接收的數(shù)據(jù)。
-
僅在使用RTOS任務(wù)通知代替隊(duì)列的情況下:接收任務(wù)可以在“阻塞”狀態(tài)下等待通知(因此不占用任何CPU時(shí)間),而發(fā)送任務(wù)不能在“阻塞”狀態(tài)下等待消息。如果發(fā)送無(wú)法立即完成,則發(fā)送完成。
5
使用方法 使用方法其實(shí)很簡(jiǎn)單,只要你會(huì)使用RTOS的隊(duì)列、信號(hào)量,基本看一眼官方例子就能使用。
我這里也拿官方例子說(shuō)明一下:
/* main() 創(chuàng)建的兩個(gè)任務(wù)的原型 */static void prvTask1( void *pvParameters );static void prvTask2( void *pvParameters ); /* 處理由main() 創(chuàng)建的任務(wù)的句柄 */static TaskHandle_t xTask1 = NULL, xTask2 = NULL; /* 創(chuàng)建兩個(gè)任務(wù),來(lái)回發(fā)送通知,然后啟動(dòng)RTOS調(diào)度程序 */void main( void ){ xTaskCreate( prvTask1, “Task1”, 200, NULL, tskIDLE_PRIORITY, &xTask1 ); xTaskCreate( prvTask2, “Task2”, 200, NULL, tskIDLE_PRIORITY, &xTask2 ); vTaskStartScheduler();}/*———————————————————–*/ /* prvTask1() 使用API的“索引”版本 */static void prvTask1( void *pvParameters ){ for( ;; ) { /* 發(fā)送通知到prvTask2() ,使其脫離“已阻止”狀態(tài)。*/ xTaskNotifyGiveIndexed( xTask2, 0 ); /* 阻止等待prvTask2() 通知此任務(wù) */ ulTaskNotifyTakeIndexed( 0, pdTRUE, portMAX_DELAY ); }}/*———————————————————–*/ /* prvTask2()使用API的原始版本(不帶“索引”) */static void prvTask2( void *pvParameters ){ for( ;; ) { /* 等待prvTask1()通知此任務(wù) */ ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); /* 向prvTask1()發(fā)送通知,使它退出“已阻止”狀態(tài) */ xTaskNotifyGive( xTask1 ); }}
本文就介紹到這里,更多內(nèi)容請(qǐng)參看官方文檔:
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!