高能預(yù)警,一篇五千字長文帶你認(rèn)識RTOS系統(tǒng)
??想了解更多關(guān)于開源的內(nèi)容,請訪問:??
在學(xué)習(xí)openharmony南向開發(fā)的時候,很多小伙伴不明白學(xué)習(xí)liteos-m內(nèi)核編程代表什么,是什么意思,以及為什么要學(xué)習(xí)這個,今天以rtos的角度為新手掃盲。
什么是LITEOS-M?
根據(jù)openharmony倉庫的解釋,??文檔地址??:
OpenHarmony LiteOS-M內(nèi)核是面向IoT領(lǐng)域構(gòu)建的輕量級物聯(lián)網(wǎng)操作系統(tǒng)內(nèi)核,具有小體積、低功耗、高性能的特點,其代碼結(jié)構(gòu)簡單,主要包括內(nèi)核最小功能集、內(nèi)核抽象層、可選組件以及工程目錄等,分為硬件相關(guān)層以及硬件無關(guān)層,硬件相關(guān)層提供統(tǒng)一的HAL(Hardware Abstraction Layer)接口,提升硬件易適配性,不同編譯工具鏈和芯片架構(gòu)的組合分類,滿足AIoT類型豐富的硬件和編譯工具鏈的拓展。
LiteOS-M內(nèi)核就是一款RTOS,關(guān)于RTOS內(nèi)核是這樣描述的:
實時操作系統(tǒng)(Real-time operating system, RTOS),又稱即時操作系統(tǒng),它會按照排序運行、管理系統(tǒng)資源,并為開發(fā)應(yīng)用程序提供一致的基礎(chǔ)。
實時操作系統(tǒng)與一般的操作系統(tǒng)相比,最大的特色就是“實時性”,如果有一個任務(wù)需要執(zhí)行,實時操作系統(tǒng)會馬上(在較短時間內(nèi))執(zhí)行該任務(wù),不會有較長的延時。這種特性保證了各個任務(wù)的及時執(zhí)行。
RTOS有什么特點?
維基百科上關(guān)于實時性的定義,??地址??
實時運算(Real-time computing)是計算機科學(xué)中對受到“實時約束”的計算機硬件和計算機軟件系統(tǒng)的研究,實時約束像是從事件發(fā)生到系統(tǒng)回應(yīng)之間的最長時間限制。實時程序必須保證在嚴(yán)格的時間限制內(nèi)響應(yīng)。
實時操作系統(tǒng)中都要包含一個實時任務(wù)調(diào)度器,這個任務(wù)調(diào)度器與其它操作系統(tǒng)的最大不同是強調(diào):嚴(yán)格按照優(yōu)先級來分配CPU時間,并且時間片輪轉(zhuǎn)不是實時調(diào)度器的一個必選項。
提出實時操作系統(tǒng)的概念,可以至少解決兩個問題:
- 一個是早期的CPU任務(wù)切換的開銷太大,實時調(diào)度器可以避免任務(wù)頻繁切換導(dǎo)致CPU時間的浪費;
- 另一個是在一些特殊的應(yīng)用場景中,必須要保證重要的任務(wù)優(yōu)先被執(zhí)行。
在這樣的背景下,實時操作系統(tǒng)就被設(shè)計出來了,典型的實時操作系統(tǒng)有VxWorks,RT-Thread,uCOS,QNX,WinCE等。
實時任務(wù)調(diào)度器是實時操作系統(tǒng)的一個必選項,但不代表只要設(shè)計出來一個實時調(diào)度器就足夠了。事實上設(shè)計一個實時調(diào)度內(nèi)核并不是一個多么復(fù)雜的任務(wù),實時操作系統(tǒng)的特性是在整個操作系統(tǒng)的設(shè)計思路上都要時刻關(guān)注實時性。
這些設(shè)計思路包括:
為什么要使用RTOS系統(tǒng)?
為什么搞清楚為什么使用RTOS系統(tǒng),首先來看一下傳統(tǒng)的芯片上開發(fā)程序是如何的?
可以看到,系統(tǒng)靠一個大while循環(huán)進行包裹,然后通過判斷各種各樣的flag或者計數(shù)值進行輪詢處理,在這種編程框架上,如果我們的產(chǎn)品的功能比較簡單,確實可以這樣做,而且需要這么做,不建議引入rtos操作系統(tǒng),因為那會帶來額外的資源消耗以及封裝.上面這種編程范式業(yè)界一般稱為前后臺系統(tǒng)。
但是在當(dāng)今物聯(lián)網(wǎng)時代,我們的產(chǎn)品功能已經(jīng)比較復(fù)雜,而且出現(xiàn)了更多的交互方式比如觸摸屏,無線網(wǎng)絡(luò)數(shù)據(jù)處理等等,并且在某些情況下產(chǎn)品的響應(yīng)也需要很快,如果采用這樣的while循環(huán)的方式無疑就不滿足要求了.仔細(xì)看,假如出現(xiàn)某個操作在flag_a里面比較耗時,那么整個系統(tǒng)的節(jié)奏也會被拉慢,那么這時候就需要一款RTOS系統(tǒng)來進行管理了。
那么我們來看一下通常RTOS系統(tǒng)的編程范式:
可以看出在RTOS的編程框架下,不再是一個大的while去進行個種flag的輪詢,產(chǎn)品所做的操作被分成了一個個的線程或任務(wù),線程或任務(wù)又通過所謂的事件或者消息進行傳遞數(shù)據(jù),整個系統(tǒng)的運行不再由一個flag去區(qū)分時間片,而且通過線程間的同步或通信機制去進行高效的配合,所以能夠應(yīng)對物聯(lián)網(wǎng)時代產(chǎn)品功能日趨豐富,場景越來越復(fù)雜的情況。
RTOS這么好,好學(xué)嗎?
答案:其實rtos系統(tǒng)是不好學(xué)的,因為RTOS相比傳統(tǒng)的單片機裸機開發(fā)方式,多了很多概念,比如線程啊,線程同步啊,互斥鎖啊,消息隊列啊等等,聽起來就很復(fù)雜,但是我們學(xué)習(xí)一定要掌握一定的思路,任何事物或者機制的出現(xiàn)都是有原因的,簡單概括一下,RTOS系統(tǒng)間各概念是如下關(guān)系:
- 因為要實現(xiàn)RTOS系統(tǒng),就必須引入多線程或多任務(wù)的概念。
- 多任務(wù)編程比單線程復(fù)雜,多線程切換就產(chǎn)生了上下文的保存恢復(fù)和資源訪問。
- 為了保存上下文就必須有線程棧。
- 在線程棧之外又有全局棧,多線程程序訪問全局棧就產(chǎn)生了數(shù)據(jù)一致性問題,為了解決數(shù)據(jù)一致性問題就有了線程間同步。
- 為了避免過度使用全局棧要讓線程間共享資源就又有了線程間通信的方式。
- 既然可以多線程了那中斷就可以實現(xiàn)快如快出了,中斷分為了上下片。
可以看出來這主要的知識點都是一環(huán)套一環(huán),新手如果不理解這其中的因果關(guān)系,而是一章一章的去看教程很容易產(chǎn)生云山霧罩的感覺,下面就幾個主要知識點做簡單總結(jié),鑒于篇幅和理解深度的原因,有的地方只能是點到為止,有說錯地方,還望各位踴躍指正。
線程是什么?多線程又是什么?為什么要多線程?
通俗來講,線程就是任務(wù),系統(tǒng)為了實現(xiàn)多任務(wù)就要引入多線程。
那么怎么形容這個任務(wù)呢:領(lǐng)導(dǎo)叫你給客戶倒杯水可以認(rèn)為是一個任務(wù),給孩子在網(wǎng)上買一包紙尿褲可以是一個任務(wù),說人話就是事情,那么對應(yīng)到計算機甚至是我們的嵌入式系統(tǒng)里面,任務(wù)就是:點個燈?發(fā)送一串串口數(shù)據(jù)或者是執(zhí)行一次復(fù)位重啟也能算一個任務(wù)。
那么多線程其實省略了"同時執(zhí)行"這幾個字,為什么多線程,顯然是為了執(zhí)行更多的任務(wù).那有同學(xué)要說:以前我們不帶系統(tǒng)的時候也可以做到一個板子做多個事情呀.哈哈,別忘了,這里面還有一個實時呢,關(guān)于這部分內(nèi)容可以查看一下關(guān)于前后臺系統(tǒng)和實時系統(tǒng)的解析。
那多線程的程序是怎么運行的?中斷是什么線程?
其實在單核芯片內(nèi)部多線程并不是同時運行的,而是根據(jù)系統(tǒng)調(diào)度規(guī)則保持在某一個時刻只有一個線程在運行,那有同學(xué)就問了,什么是調(diào)度?我們可以理解為調(diào)控和分配,打個比方,春晚有一個小品叫<<裝修>>,黃大錘先拿大錘破磚,再拿小錘摳縫,這個從大錘變到小錘就是一個調(diào)度,發(fā)現(xiàn)墻破的差不多了,停止大錘活動,小錘上場繼續(xù).對應(yīng)到計算機上,就是一個任務(wù)執(zhí)行完了,系統(tǒng)自動將當(dāng)前任務(wù)停止,轉(zhuǎn)而去執(zhí)行另一個線程。
中斷不屬于任何一個線程,中斷的優(yōu)先級大于所有的線程,拿手機去對比:哪怕你馬上要吃雞成功或者王者榮耀正在進行激烈的團戰(zhàn),只要有電話進來,你的手機都會把游戲停止,讓你決定是否接聽電話,因為在手機的設(shè)定中,接打電話是最高優(yōu)先級事件,所有的其他任務(wù)都得給這個事件讓路。
什么是搶占式優(yōu)先級調(diào)度和時間片輪轉(zhuǎn)調(diào)度?
很好理解,搶占式就是某一任務(wù)很緊急,必須要打斷其他線程的運行,舉例:你正在吃飯或者正在走路的時候,突然肚子痛要拉肚子,那這時候吃飯和走路的優(yōu)先級就沒有這個要去上廁所的優(yōu)先級高,那么我們說:吃飯和走路被上廁所搶占運行。
而時間片輪轉(zhuǎn)調(diào)度就更好理解:給某一線程分配一個最大運行時間,時間一到,不管現(xiàn)在的任務(wù)進行的如何,如果還有同等優(yōu)先級的任務(wù)要執(zhí)行,就轉(zhuǎn)去執(zhí)行這個同等優(yōu)先級的任務(wù),可以結(jié)合下面寫作業(yè)的例子去理解。
再舉個不形象的例子:語文課和數(shù)學(xué)課地位同等重要,所以在學(xué)校不會有語文老師去搶數(shù)學(xué)老師的課時,頂多拖下堂.—>這就是時間片輪轉(zhuǎn)調(diào)度,而體育課相比之下數(shù)學(xué)語文這種"正課"顯得重要,所以很多時候數(shù)學(xué)老師會跟你們說,這節(jié)體育課改上數(shù)學(xué).–>優(yōu)先級搶占。
調(diào)度理解了,那線程棧是什么,上下文又是什么?
打個比方,上學(xué)的時候我們學(xué)習(xí)語文數(shù)學(xué)英語,老師規(guī)定作業(yè)要這樣寫:寫一分鐘語文,一分鐘的數(shù)學(xué),然后再去寫一分鐘英語,這樣不段循環(huán),直到所有的作業(yè)都寫完.好等到你開始寫了,先打開語文作業(yè)本,找到對應(yīng)的頁碼和題目,開始寫作業(yè),嘀嘀嘀一分鐘很快到了,你不得不把語文作業(yè)本合起來,標(biāo)記一下寫到了哪里,然后打開數(shù)學(xué)作業(yè)本,找到對應(yīng)的頁碼和題目,開始寫作業(yè),嘀嘀嘀一分鐘又到了,你又不得不合上數(shù)學(xué)作業(yè)本,把頁碼和題目標(biāo)記起來,開始同樣的動作去寫英語,等到英語的時間到了之后,又得去把語文作業(yè)本打開,找到剛剛記錄的頁碼和題目繼續(xù)寫.那么我們想象一下有三個竹筒,里面分別裝有語文,數(shù)學(xué),英語的作業(yè)相關(guān)的信息,這個筒子就是線程棧,那這個筒子里記錄的頁碼和題目就是上下文信息.簡單說來:上下文用于保存和恢復(fù)線程運行的狀態(tài)和結(jié)果等信息,是多線程程序的基石,要實現(xiàn)多任務(wù),就必要有上下文。
線程同步是什么,線程間通信又是什么,他們是干嘛的?
上文說了,每一個線程的棧區(qū)和上下文是分別獨立存儲的.現(xiàn)在有個問題:怎么確定線程執(zhí)行的順序,和對資源訪問的一致性?就好比不對里有嚴(yán)格的紀(jì)律和軍銜來限制士兵的活動一樣,rtos系統(tǒng)基于優(yōu)先級來控制線程的運行順序,那總不能高優(yōu)先級程序一直在運行吧,低優(yōu)先級任務(wù)得不到執(zhí)行叫什么多線程。
放心,這些問題早都有辦法了,高優(yōu)先級的任務(wù)可以通過調(diào)用定時器來達到阻塞當(dāng)前運行,交出cpu的控制權(quán),當(dāng)控制權(quán)交出后,其他線程得以運行,那么用定時器的阻塞有一個問題:假如線程是要獲取某個東西,當(dāng)時當(dāng)定時器到期了還沒獲取到或者說不知道這個東西是不是有效的,那怎么辦?
此時,就要用到信號量或者消息隊列等線程同步或者線程通信等手段了。
區(qū)別在于:線程同步不攜帶數(shù)據(jù),而線程通信攜帶數(shù)據(jù)。
舉個線程間同步的例子,當(dāng)某個線程正打算要讀取某個變量的值,突然被一個高優(yōu)先級的線程搶占,這個高優(yōu)先級的任務(wù)正好會改變這個變量的值,然后高優(yōu)先級線程執(zhí)行完畢,又輪到這個線程去執(zhí)行,然后這個變量因為被變掉了,所以可能造成該線程的錯誤運行,這個時候就需要用到線程同步了,在當(dāng)前程序在訪問或者修改某個變量或者外設(shè)的時候,用信號量等手段把操作保護起來,這樣當(dāng)正好發(fā)生了高優(yōu)先級線程搶占式運行的時候,能夠提示一下:嘿哥們我還沒完事呢,你等會哈,這時候等待還是不等,等多久就由高優(yōu)先級去選擇了.通俗的比方:現(xiàn)在有一個公廁只有一個坑,列兵小王正在蹲坑,這時候團長來了,因為不敢得罪團長,小王不得不讓出坑位讓團長先上,有了線程同步之后,相當(dāng)于公廁上加了一個鎖,小王上廁所的時候,把門一鎖,管你是多大的領(lǐng)導(dǎo)也得給我等著,哈哈。
再舉個線程間通信的例子:食堂有王姨和張姨,王姨管打飯,張姨管打菜,去食堂吃飯先打飯再打菜,如果不建立起一個良好的線程通信機制,可能就是這樣的:張姨不知道王姨現(xiàn)在是在打菜還是等自己的飯,王姨打完一個菜就不知道干嘛了,玩會手機或者跟別人嘮嘮閑嗑都有可能.或者是張姨不知道王姨打飯速度不夠快,使勁裝飯把王姨的工作臺都占滿了,或者張姨打菜慢王姨打完飯老來問飯好了沒有(不停去訪問某一變量看是否有值),這樣的情況顯然是不合理的.那么用線程間通信,王姨打完飯就在那邊阻塞等飯送過來,張姨裝完飯就放在某個地方,等王姨打完一個菜再裝下一個,這里面的盤子就相當(dāng)與消息隊列或者郵箱里的一份數(shù)據(jù),雙方都需要這個數(shù)據(jù),然后通過線程間通信機制正確的執(zhí)行自己的任務(wù)。
中斷的上半部和下半部又是什么?不帶rtos系統(tǒng)的程序怎么沒有這說法?
通常在嵌入式編程中,中斷的處理需要遵循快進快出的原則,而不帶rtos系統(tǒng)因為沒有多任務(wù)編程的概念,幾乎不會采用上半部和下半部編程(某些狀態(tài)機框架可以實現(xiàn)),通俗來說:上半部就是得到數(shù)據(jù),下半部就是處理數(shù)據(jù),像這樣把數(shù)據(jù)的產(chǎn)生和數(shù)據(jù)的處理分成上下兩層的方式就叫上半部和下半部,所以要是現(xiàn)在上半部和下半部編程,最好就是采用多線程編程,因為可以讓一個線程用消息隊列或者郵箱等方式阻塞在數(shù)據(jù)接收的地方去等待數(shù)據(jù)過來,在阻塞期間不影響其他線程的運行。
RTOS這么好,一定要學(xué)嗎,一定要用嗎?
前面寫了很多字了,那么以這個問題來結(jié)尾吧:在當(dāng)今物聯(lián)網(wǎng)時代,從事單片機或者嵌入式編程一定要學(xué)會一款RTOS系統(tǒng),而且基于RTOS的這些特性,一個很明顯的特點是:會了一款,其他的自然就會了,因為RTOS就那么多東西,不同的RTOS只是API接口長的不一樣,在實現(xiàn)機制上也有些許差別,但是思路不統(tǒng)一的,所以,一定要學(xué)會RTOS編程,即使在工作中用不上,學(xué)習(xí)的過程中用來擴展思維也是不一樣的,學(xué)編程,思維很重要。