OS和裸機的區別
OS即(operating system)操作系統,比如我們常用的windows系統,mac系統,android系統,ios系統,linux系統等,都屬於操作系統。操作系統的本質是一個特殊的軟件,它直接管理硬件,同時為各個應用程序划分資源(內存,堆棧,時間片等),並提供控制(調度,同步)。不管是計算機還是單片機,在任意時刻都只能運行一段代碼,頂多是運行速度上會有差距,為什么我們能夠在電腦上打開多個軟件同時流暢的使用,就需要歸功於操作系統對於軟件的控制,操作系統會將各個應用程序抽象成進程,給每個進程獨立的分配資源,同時對他們進行調度,使得每一個進程都仿佛是在獨占整個計算機。
以下圖為例,該圖較為清晰的反映了硬件,操作系統和應用程序之間的層次關系。可以發現操作系統operating system位於硬件computer hardware之上,應用程序application programs又位於操作系統operating system之上,各個應用程序如compiler(編譯器),assembler(組譯器),text editor(文本編輯器),database system(數據庫系統)等都被抽象成了用戶(user)。
另外,雖然目前主流的操作系統都是有圖形界面便於用戶交互,但是實際上圖形界面並不是一個操作系統必要的一環,熟悉linux系統的同學會知道,linux可以通過命令行來完成幾乎所有的操作。操作系統最本質的功能還是作為一個連接計算機硬件和應用程序的中間連接者。
由於單片機的資源比較少,一般在單片機上運行的操作系統具有功能精簡,實時性強的特點,也被稱為RTOS(real time operating system)實時操作系統。單片機上的RTOS非常多,國外常見的RTOS包括FreeRTOS,uCOSⅡ等,國內這幾年做的RT-Thread操作系統也發展的很好,阿里和騰訊蹭物聯網熱點弄了些AliOS啥的亂七八糟的東西,但是好像沒啥動靜,個人感覺國內真正有競爭力的還是RT-Thread。
沒有運行OS的計算機統稱為裸機,一般我們利用中斷和循環構建前后台系統完成的工程都是裸機工程,中斷是前台,針對各種突發的中斷源進行及時響應,循環是后台,穩定執行一些常駐的重復性工作。在裸機工程中,編寫者對於代碼的執行情況是一清二楚的,只要編寫者清楚中斷到來的時刻,就能知道每一時刻中單片機在執行哪段代碼,另外裸機工程由用戶手動分配堆棧,所以總體上裸機工程是完全可控的,這也使得裸機的調試難度比較低,但是當工程復雜,耗時、耗資源的任務多時,裸機工程必須在中斷中編寫復雜的邏輯或者執行耗時的任務,這就會導致裸機工程的執行效率非常低下,響應喪失實時性。
而OS則不一樣,由於任務調度和堆棧分配都是由OS來完成的,編寫者並不知道任意時刻OS內部的執行情況,因此如果OS的執行出了問題,調試難度是比較大的,很多時候需要借助特殊的調試工具幫助查找問題,比如FreeRTOS就有專用的調試工具FreeRTOSViewer。
但是OS的好處是能夠有效的提高單片機的資源利用效率,一方面是空間利用效率高,假設單片機的RAM實際大小為1Mb,一個任務運行需要256Kb的存儲空間,則對於裸機工程來說,能夠運行的任務數量上限就只有1Mb/256Kb等於4個,而對於OS來說,雖然同時運行的任務數量同樣最多只能有4個,但是OS可以通過釋放執行完畢的任務的資源來空出新的存儲空間,讓新的任務得以執行,能夠執行的總任務數量大大高於裸機。
另一方面是時間利用率高,之前說過,裸機工程最大的問題就是在工程復雜時,不得不往中斷中增加一些執行起來很耗時的代碼,繼而導致實時性大大下降,比如工程中有一個定時器中斷和一個串口中斷,兩個中斷到來的間隔時間位1ms,定時器中斷的優先級高於串口中斷,只要定時器中斷中的代碼需要大於1ms的時間來執行,就會導致串口中斷無法得到響應。可以想象當中斷數量更多,各個中斷之間的間隔時間更短,而需要執行的任務耗時更長的情況下,裸機工程跑起來會是個什么慘狀。而使用OS時,耗時的代碼全部放到任務中,交給OS來調度;中斷中只需要執行耗時短的重要代碼,這樣中斷就能夠得到及時的響應,即使有多個復雜,耗時的任務也能夠實時的進行處理,下圖就是一個典型的RTOS執行的時序,可以看到中斷消耗的時間很短,耗時的代碼都被放到任務中去了。
此外OS還可以提供一些裸機不具備的功能,比如信號量,消息隊列,任務通知等,用來管理復雜情況下的資源分配或者進程同步。以信號量為例,信號量的功能是實現各個任務對臨界資源的互斥訪問,比如一輛步兵車采用雙板方案,雲台和底盤各有一塊開發板,兩板采用串口進行通訊。下板需要將底盤yaw軸電機角度信息和功率信息發送給上板,yaw軸電機角度的發送和功率信息的發送各自在一個定時器中斷和一個串口中斷中進行,這時上下板通訊所使用的串口就是一個臨界資源,必須采用信號量進行保護,即當一個任務正在訪問通訊串口時,會占有信號量,另一個任務到來之后必須處於阻塞狀態,等待上一個任務訪問完畢,釋放信號量后才能訪問通訊串口。
如果不進行保護的話,有可能會出現這種情況:串口中斷中發送的功率信息只發送到一半 ,定時中斷就緒了,串口中斷被打斷,剩下的一半發送的信息變成了yaw軸電機角度信息,兩個信息一拼之后就變成了沒有意義的亂碼,發送給上板之后會引發各種奇奇怪怪的問題。
最后,現在的RTOS基本都是有自己的生態圈的,各種開發商會基於RTOS提供各種便利的組件,包括網絡,藍牙,GUI圖形界面,文件管理系統等。選擇使用OS開發的話就能夠直接在工程中調用這些組件的接口,並且有豐富的文檔支持,而如果是裸機的話就得自己造很多輪子,過於浪費時間精力。
所以在RM比賽中,到底有沒有必要跑OS?我覺得其實是沒有必要的,按照上文所說,除非出現工程過於復雜,耗時耗資源的任務過多的情況下,裸機才會有比較嚴重的問題,其他情況下裸機的可控性和調試方便程度都優於OS,而RM比賽中,一般一段中斷里面的代碼不過幾百行,執行起來的耗時根本到不了毫秒級,用OS也並不能體現出任何優勢,另外上面舉例的信號量處理臨界資源競爭的問題,其實兩個中斷撞到一起的概率非常非常的小,就算有也可以通過合理的設置中斷優先級,或者代碼邏輯來避免問題。
但是沒有必要不等於不能夠或者不應該上OS,如果編寫者對OS的機制比較熟悉的話,使用OS就能夠有非常好的編碼體驗,整個工程的抽象度得到了提升,代碼的邏輯分層更加的清晰,不同兵種之間進行代碼遷移也會比較容易。目前使用OS的參賽隊還是很多的,我觀摩過幾個學校的代碼,還是很有水平的。
任務調度機制
這里以FreeRTOS為例,介紹一下OS的任務調度機制。
我們先簡單介紹一下進程的概念,對於進程,我們很難找到一個准確的定義,一般我們會將程序的一次執行當成一個進程,更准確的說,我們將一個程序在一個數據集合上的運行過程當成一個進程,這說明進程包含着動態的概念,一段程序執行時,我們一般划分成三個階段,開始執行--->執行中--->執行完成。這也恰好對應了進程的工作狀態:就緒態--->運行態--->掛起態。
進程除了以上三種狀態,還有一個重要的狀態被稱位阻塞態(Blocke),對應的是一個程序執行到一般時被暫停的狀態。
在FreeRTOS中,進程的四種基本工作狀態是就緒態(Ready),運行態(Running),阻塞態(Blocked)和掛起態(Suspended),各個狀態的相互轉換關系如下圖:
我們編寫一段代碼,來展示OS下的任務代碼編寫和裸機代碼編寫之間的區別:
void green_led_task(void const * argu)
{
while(1)
{
HAL_GPIO_TogglePin(GREEN_LED_PORT,GREEN_LED_PIN);
osDelay(100);
}
}
這段程序的功能是控制一個綠色LED閃爍,如果我們在普通的裸機工程中將其作為一個函數調用,程序就會一直卡在這段閃爍的循環里,不會執行后續的代碼,假如我們再寫一個紅色LED閃爍的代碼,在裸機工程中調用,紅色LED是不會閃爍的(這里我使用的是OS中的延時函數osDelay,裸機工程中對應的是HAL_Delay函數)。
void red_led_task(void const * argu)
{
while(1)
{
HAL_GPIO_TogglePin(RED_LED_PORT,RED_LED_PIN);
osDelay(100);
}
}
但是我們通過如下的代碼將上面兩個函數注冊為兩個進程之后:
osThreadDef(GreenLEDTask, green_led_task, osPriorityNormal, 0, 128);
green_led_task_t = osThreadCreate(osThread(GreenLEDTask), NULL);
osThreadDef(RedLEDTask, red_led_task, osPriorityNormal, 0, 128);
red_led_task_t = osThreadCreate(osThread(RedLEDTask), NULL);
OS就會自動將上面兩個代碼進行調度,最后我們看到的結果是紅綠LED一起以1s為周期閃爍。
OS的調度過程是這樣的——當綠色LED閃爍進程green_led_task執行到osDelay處時,OS會將該進程由運行態變成阻塞態,直到500ms之后才會將其恢復為就緒態。當綠色LED閃爍進程處於運行態時,如果紅色LED閃爍進程red_led_task在此時就緒了,就需要優先等待綠色LED閃爍進程從運行態變成阻塞態,才可以從就緒態變成運行態。
所以調度的實質就是OS按照某種調度算法的原則,安排各個進程的運行狀態,使得它們以近乎“並行”的方式得到執行。關於調度的具體算法,這里不加以詳細的介紹,主要是三個:先來先服務(FCFS)調度算法,優先級調度算法和時間片輪轉調度算法。三種算法同時執行,合作完成任務調度功能。
我其實很久之前開了一個FreeRTOS的教程坑,已經寫了四節,其中第三節有詳細的介紹OS的進程調度算法,有興趣的同學可以看一看。
https://www.cnblogs.com/sasasatori/p/12231918.html
總結
這是Robomaster電控入門系列教程預定的最后一篇,各位讀者如果還有想要了解的東西的話可以私信我,如果我覺得有必要寫一寫,就會再拓展一下這個系列教程。
我從4個月前開始挖了這個系列的坑,斷斷續續的寫到了現在。教程的目的是服務各位新入RM坑的坑友,有很多同學通過評論,私信和我討論問題,讓我感到自己的這個教程還是幫助到了一些人的。參加RoboMaster這個比賽是我本科生涯中最為濃墨重彩的一筆,我也想通過這種方式來反哺這個為我帶了許多成長的比賽,以上,感謝你能夠看到這里,謝謝