TencentOS浅学过程记录
TencentOS淺學過程記錄
- 前言
- 一、RTOS
- 二、學習資料來源
- 三、初步學習過程中的疑難問題解決
- 任務調(diào)度以及輪詢時間片
- 消息隊列與郵箱隊列
- 互斥鎖
- 任務中為什么一定要加while(1)循環(huán)
- 內(nèi)存管理
- 三、實操問題解決
- 使用TencentOS中的shell(即**CLI命令行**)
- RTOS新手學習一定要注意**堆棧大小**
- 使用動態(tài)內(nèi)存時記得要去tos_config.h文件中修改配置
- 提醒
前言
大三下生產(chǎn)實習作業(yè),基于LoRaWAN的物聯(lián)網(wǎng)應用,剛好有一個選題是關于RTOS的,之前對于RTOS也有過一些了解,但是一直沒有真正的學習使用過,剛好借這個機會學習一波。(這里主要分享一下我在對于騰訊TencentOS學習使用過程中的一些收獲與問題解決過程)
一、RTOS
實時操作系統(tǒng)的概念這里不再贅述。相較于之前順序執(zhí)行的裸機編程,RTOS多任務執(zhí)行以及阻塞機制使得程序執(zhí)行效率更高,一些沒有必要執(zhí)行的代碼可以利用阻塞機制使它僅在必要的時候執(zhí)行,任務重要性可以按照優(yōu)先級分配,整體看起來就是我們想要哪些代碼執(zhí)行,哪些代碼不執(zhí)行都是可以控制調(diào)節(jié)的,這樣可以使重要的任務一直得到有效執(zhí)行,這樣程序執(zhí)行效率有很大提高。
二、學習資料來源
當前開源的RTOS非常多,剛開始我有了解到RT-Thread,Freertos,TencentOS,uCOS等等,但是對于新手大佬推薦RT-Thread比較多,畢竟是國產(chǎn)開源,相關資料手冊等等都是中文編寫,目前使用也比較廣泛,各處學習網(wǎng)站也有非常多的問題解決博客。當然TencentOS也是中文資料,只不過目前使用沒幾年使用量不是特別大,除過騰訊開源庫中的資料其他可借鑒的博客也比較少。至于為什么選擇TencentOS,作業(yè)時間留的有點短,杰杰大佬說TencentOS源碼實現(xiàn)非常精簡,沒有特別復雜的數(shù)據(jù)結(jié)構(gòu)在里面,對于新手接觸RTOS,簡單了解體驗RTOS中的一些任務通信機制非常友好好上手(當然C語言基礎得不錯)(不同公司開源的RTOS內(nèi)部任務同步以及通信機制大同小異),作業(yè)時間只有一個月,TencentOS正合我意。
我在學習過程中主要借鑒的學習資料有:
以上以Github開源庫為主要資料,杰杰大佬的專欄寫于2019年左右,但是目前TencentOS在一些方面更新了很多,總之就是許多代碼不是以前的代碼了,但是大同小異,杰杰大佬專欄中對于郵箱,信號量,互斥鎖,事件等等的講解非常有助于對TencentOS中相關任務機制的理解。由于更新比較多,學習者在學習過程中還是有必要閱讀一下相關源碼的(會C就可以,源碼參考杰杰大佬的專欄會相對比較好理解)。
GitHub中資料比較齊全
其他的在CSDN上也是零零散散能找到多少是多少。
三、初步學習過程中的疑難問題解決
這里主要指在運行github提供的內(nèi)核開發(fā)指南中的例程源碼時,嘗試性的修改代碼產(chǎn)生的一系列問題以及對一些概念的理解。
任務調(diào)度以及輪詢時間片
1.任務調(diào)度:每一次任務調(diào)度即進入knl_sched()的時候,系統(tǒng)會去查找就緒態(tài)任務中優(yōu)先級最高的任務。如果優(yōu)先級最高的任務不止一個(相同優(yōu)先級的任務會按順序掛載在同一個列表中),系統(tǒng)會在同一優(yōu)先級任務列表中選取第一個進行執(zhí)行;
2.輪詢時間片處理:當進入輪訓時間片處理程序時,系統(tǒng)會獲取當前任務以及當前任務的優(yōu)先級,然后判斷該優(yōu)先級列表中是否還有同優(yōu)先級的任務處于就緒態(tài),如果有就按照輪詢時間片機制,輪詢時間片數(shù)值依次減少。如果沒有同優(yōu)先級任務處于就緒狀態(tài),系統(tǒng)將不再執(zhí)行輪詢時間片機制。(這也就是說輪詢時間片僅在當任務就緒列表中同一個優(yōu)先級的任務有兩個以上時才會起作用,否則是不會起作用的);在輪詢時間片起作用的情況下,當一個任務執(zhí)行時間超出輪詢時間片的時間設定后,系統(tǒng)會把相同優(yōu)先級任務列表中第一個任務調(diào)換到最后一個,那么下一次系統(tǒng)調(diào)度即執(zhí)行knl_sched()時取最高優(yōu)先級的第一個任務(原先的任務插到了列表中的最后一個,這個時候已經(jīng)實現(xiàn)了任務被輪詢時間片強行切到了另外一個任務)
代碼注釋簡單如下:
總結(jié):這個功能很雞肋,大部分時間沒什么用,而且這是一種強行切換,如果你的任務中存在互斥鎖,強行切換導致互斥鎖沒有來的及釋放,一定會出問題。(總之這是一種強行切換,一般情況下只有在任務中不加os_delay()時才會用到,而且在切換時不對原先任務中的互斥鎖等同步機制進行應急處理)。感覺沒什么用。
消息隊列與郵箱隊列
TencentOS里面提供了消息隊列,郵箱隊列以及優(yōu)先級消息隊列,優(yōu)先級郵箱隊列。底層實現(xiàn)主要是兩個分別是環(huán)形隊列和優(yōu)先級隊列來實現(xiàn),這兩個區(qū)別就是,環(huán)形隊列按照先入先出,優(yōu)先級隊列寫入時會記錄寫入的優(yōu)先級,彈出時會按照優(yōu)先級高低先后彈出。另外就是消息隊列和郵箱隊列的區(qū)別,消息隊列僅傳遞指針,郵箱隊列可以傳遞較大的內(nèi)存塊,底層實現(xiàn)都是環(huán)形隊列,只不過消息隊列存入的是指針即一個32位的地址,郵箱隊列可以存入一個結(jié)構(gòu)體中的所有數(shù)據(jù)。
uint8_t msg_pool[MESSAGE_MAX * sizeof(void *)];//消息隊列存儲池MESSAGE_MAX*地址占內(nèi)存大小(更小)typedef struct mail_st {char *message;int payload; } mail_t; uint8_t mail_pool[MAIL_MAX * sizeof(mail_t)];//郵箱隊列存儲池MAIL_MAX*結(jié)構(gòu)體占內(nèi)存大小(更大)我的理解:郵箱隊列可以保證數(shù)據(jù)存入郵箱,數(shù)據(jù)沒有被接收之前不會丟失,但是消息隊列中的地址數(shù)據(jù)未被接收之前如果該地址指向的內(nèi)存數(shù)據(jù)發(fā)生變化,那么接收到之后的數(shù)據(jù)可能并不是你想傳遞的數(shù)據(jù)。(如果數(shù)據(jù)被更改時間間隔足夠長的話可以使用)。這個時候我覺得消息隊列還挺雞肋的,我還沒有想到一個消息隊列的合理利用場景,糾結(jié)于傳送地址,但地址數(shù)據(jù)發(fā)生改變不就亂了,希望大佬看到可以指點一下。這塊指出一下,不同的RTOS如騰訊還有FreeRTOS中的同步及通信機制原理上差不多但是使用時還是有區(qū)別的,比如FreeRTOS中的消息隊列和TencentOS中的郵箱一樣傳遞的是數(shù)據(jù)不是指針。
互斥鎖
互斥鎖中有一個優(yōu)先級翻轉(zhuǎn)的概念,優(yōu)先級翻轉(zhuǎn)會對程序中任務執(zhí)行產(chǎn)生很大的危害,高優(yōu)先級的任務得不到有效執(zhí)行,舉一個簡單的優(yōu)先級翻轉(zhuǎn)的例子:有四個任務A,B,C,優(yōu)先級分別為1,2,3,并且A與C在爭奪互斥鎖,某一時刻A,B均處于阻塞態(tài),任務C獲取到互斥鎖,此時鎖處于閉鎖狀態(tài)。接下來A任務到達就緒態(tài),因為優(yōu)先級高搶斷C執(zhí)行但是獲取不到鎖進入阻塞態(tài)。這是B到達就緒態(tài),由于B不需要互斥鎖且優(yōu)先級高于C,所以B搶斷C執(zhí)行。在這個過程中A想要執(zhí)行就必須等待B執(zhí)行完之后切換到C,C執(zhí)行完才能輪到A。然而A是優(yōu)先級最高的任務,這樣會使得系統(tǒng)中優(yōu)先級最高的任務無法得到有效執(zhí)行,嚴重的話會導致程序崩潰。
TencentOS采用了優(yōu)先級繼承策略,當任務A申請鎖的時候,發(fā)現(xiàn)鎖在C的手里且C的優(yōu)先級低于A的優(yōu)先級,那么立馬將C的優(yōu)先級提高到和A等同的優(yōu)先級,那么C執(zhí)行過程中就不會被B打斷,即A只要等C執(zhí)行完就可以執(zhí)行了,不用等待B的執(zhí)行。這樣一定程度上削弱了優(yōu)先級翻轉(zhuǎn)的危害。
當信號量為1的時候也可以當做互斥鎖用,但是由于信號量沒有設計優(yōu)先級繼承的東西,所有有可能造成優(yōu)先級翻轉(zhuǎn)。
任務中為什么一定要加while(1)循環(huán)
在RTOS初學過程中,有些時候就忘記寫while(1)了,執(zhí)行后發(fā)現(xiàn)咋突然就停下來了,甚是奇怪。每個任務也是一個函數(shù),切換任務時會保存上一個任務的上下文。如果沒有while(1),程序執(zhí)行完最后一句就結(jié)束了,函數(shù)執(zhí)行結(jié)束后函數(shù)里面的變量等等都會被釋放掉,該任務就被直接銷毀了,只有讓任務執(zhí)行函數(shù)處于循環(huán)當中,函數(shù)不會結(jié)束,任務才不會被銷毀。任務才能夠在調(diào)度時保存上下文,下次從原先離開的地方繼續(xù)執(zhí)行。這也就是我們在使用過程中看到任務函數(shù)只在while(1)內(nèi)部執(zhí)行,切換任務從哪里離開,下次就從哪里開始。
內(nèi)存管理
TencentOS中有兩種內(nèi)存管理,分別是靜態(tài)內(nèi)存管理和動態(tài)內(nèi)存管理。
靜態(tài)內(nèi)存是在全局變量區(qū)開辟一塊待分配的內(nèi)存,可以對這片內(nèi)存進行管理(申請釋放定長內(nèi)存塊)。
動態(tài)內(nèi)存是指在堆區(qū)申請釋放定長內(nèi)存塊。
內(nèi)存泄漏:申請的內(nèi)存塊使用完之后一定要釋放,并且申請內(nèi)存的獲得的首地址不能丟失,相關內(nèi)存泄漏的知識可以參考其他博文。
動態(tài)內(nèi)存管理參考示例:
總結(jié):按照我的理解,動態(tài)內(nèi)存的使用是在當原先任務堆棧大小設置的較小,而實際任務中需要使用更大的內(nèi)存,也就是說,原先任務堆棧大小不能滿足程序運行的需要,為了程序正常運行,我們需要從堆區(qū)開辟一塊動態(tài)內(nèi)存來使得程序正常運行,程序運行完之后把內(nèi)存釋放掉用于其他。
三、實操問題解決
這塊主要是我在完成生產(chǎn)實習作業(yè)過程中即實際開始自己碼代碼過程中遇到的疑難問題解決。
使用TencentOS中的shell(即CLI命令行)
命令行類似一種對話框,基本邏輯就是PC端通過串口輸入命令,單片機破解命令執(zhí)行對應的功能函數(shù)。TencentOS自帶shell中包括了help(查詢有哪些命令,分別是哪些功能)、ps(每個任務的堆棧大小以及實際運行中的堆棧深度),還有free(查詢程序內(nèi)存使用情況)。我們也可以添加自己的命令對單片機進行控制。(總之使用起來非常方便)
shell文件位置(開源庫中的文件)
shell中的文件全部移進去,包含一些頭文件啥的,反正都在github下載的這些文件中,找一下就有。
最后總共就這么多就夠了。
TencentOS的shell是用字符輸入輸出隊列配合信號量實現(xiàn)的。簡單來說就是你在串口助手界面輸入一個字符,shell代碼會將獲取到的字符塞入到字符隊列中去,同時信號量加一。另一邊任務函數(shù)會根據(jù)信號量的多少進行字符循環(huán)獲取保存并回傳給PC,信號量為零則進行阻塞。當任務函數(shù)檢測到有\(zhòng)n,\r時則會跳出循環(huán),將之前保存的字符與命令庫中的命令進行對比,如果對比正確則執(zhí)行響應函數(shù),對比錯誤就回傳"command not found\r\n"
核心代碼如下:
效果:
RTOS新手學習一定要注意堆棧大小
任務程序運行都是在分配的堆棧內(nèi)存中運行的,如果分配的堆棧內(nèi)存不夠,程序就會直接暴斃(進入硬件錯誤中斷)。所以新手在調(diào)試程序過程中可以嘗試:
1.硬件錯誤中斷中加入printf函數(shù)用于提醒你進入了硬件中斷(這個時候首先考慮堆棧內(nèi)存不夠問題)
2.如果直接導入了shell文件,可以通過"ps"命令直接進行獲取當前任務的吃棧深度。一般所有的任務堆棧大小盡量先設置的大一些,然后ps命令看一下最大吃棧深度有多深,最后根據(jù)最大吃棧深度調(diào)整任務堆棧大小(單片機內(nèi)存還是要省著用的)
stk size代表設定的堆棧大小,stk depth代表程序?qū)嶋H運行時所使用到的最大棧深度
3.TencentOS提供了,獲取最大吃棧深度的函數(shù)
使用動態(tài)內(nèi)存時記得要去tos_config.h文件中修改配置
可以把最初給的大小根據(jù)實際情況改大一點,否則后邊的大內(nèi)存申請會有問題。
#define TOS_CFG_MMHEAP_DEFAULT_POOL_SIZE 0xA00 // 配置TencentOS tiny默認動態(tài)內(nèi)存池大小提醒
代碼中有詳細注釋
總結(jié)
以上是生活随笔為你收集整理的TencentOS浅学过程记录的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 建设规划合理、高效便捷的现代物流中心——
- 下一篇: AMD锐龙R3 5400U性能怎么样?相