拓展?|?Rust語言在嵌入式領(lǐng)域的應(yīng)用
今天看到一篇關(guān)于Rust語言的文章,分享給大家,可以擴(kuò)展下視野。
開卷有益,恭喜你又進(jìn)步了一點(diǎn)點(diǎn)。
Rust語言是二十一世紀(jì)的語言新星。Rust被人廣泛承認(rèn)的一點(diǎn),就是因?yàn)樗苓\(yùn)行在多樣的目標(biāo)上,從桌面和服務(wù)器設(shè)備,到資源有限的嵌入式設(shè)備。
我們可以用適合來評價(jià)一門語言和技術(shù)。Rust非常適合開發(fā)嵌入式應(yīng)用,它是一種和C相仿的、能應(yīng)用于嵌入式設(shè)備開發(fā)的編程語言。
操作系統(tǒng)都是從裸機(jī)設(shè)備開始運(yùn)行的,Rust語言的這一點(diǎn)也意味著,它能很好地用于編寫操作系統(tǒng)。無論是應(yīng)用層還是內(nèi)核本身,Rust都是極富競爭力、值得投入時(shí)間的技術(shù)選項(xiàng)。二十一世紀(jì)的裸機(jī)編程語言在這個(gè)互聯(lián)網(wǎng)全面普及、性價(jià)比設(shè)備應(yīng)用更廣的時(shí)代,安全和可靠性成為一門語言必須考慮的因素。Rust語言采用移動語義,擁有嚴(yán)格的代數(shù)類型系統(tǒng)以及生命周期、所有權(quán)模型;相比傳統(tǒng)的編程語言,這些模型能在合適的時(shí)候釋放所用資源,減少漏洞的出現(xiàn)。此外,通過語義檢查,Rust能在編譯期有效尋找內(nèi)存和線程安全問題,降低開發(fā)和測試的負(fù)擔(dān)。
Rust語言是的運(yùn)行效率高、開發(fā)效率好、適用范圍廣。作為一門編譯型語言,它直接編譯輸出到匯編代碼,通常公認(rèn)裸機(jī)的Rust語言性能在C語言級別,擁有較高的運(yùn)行效率。Rust語言的開發(fā)效率很高,文檔完善、編譯器提示有幫助,能節(jié)省軟件開發(fā)所需的時(shí)間。它能應(yīng)用在多個(gè)平臺和指令集中,這包括裸機(jī)平臺;處理核、操作系統(tǒng)廠家還可以提供自己的編譯目標(biāo),無需廠家自己重新開發(fā)、提供工具鏈。
Rust語言出彩的地方在于,它向嵌入式平臺引入了大量新的編程技術(shù)。這包括了閉包、過程宏等傳統(tǒng)上用于函數(shù)式編程的技術(shù),和多態(tài)、虛函數(shù)表等面向?qū)ο笳Z言的技術(shù)。新編程技術(shù)的引入,擴(kuò)充了開發(fā)者的選擇。即使徹底理解Rust的編程概念有一定難度,但這些易用的新技術(shù),讓開發(fā)者只需閱讀實(shí)例代碼,便可快速進(jìn)入開發(fā)狀態(tài)。這些新技術(shù)的引入,是嵌入式平臺從未有過的,Rust能提高開發(fā)者的工作效率,降低平臺間遷移的學(xué)習(xí)時(shí)間和成本。
裸機(jī)上的過程宏
傳統(tǒng)用于嵌入式平臺的編程,我們加快開發(fā)速度使用的宏,常?;谡Z法字符串的替換和修改。Rust語言擴(kuò)充了宏的概念,提出了基于語法樹的“過程宏”編程方法,讓宏語法更容易使用、編寫更方便。“過程宏”是接收Rust代碼作為輸入,操作這些代碼,然后產(chǎn)生另一些代碼的過程。它和字符串的替換不同,是從語法樹到語法樹的替換。開發(fā)一個(gè)過程宏,可以使用簡單的定義過程,或者有工作量的屬性宏定義過程。簡單的定義中,我們編寫代碼,給出宏的輸入有哪些,要翻譯到哪些輸出代碼,這樣就完成了一個(gè)宏的定義。屬性宏定義則允許完成語法樹分析、代碼生成甚至代碼優(yōu)化的過程,就需要編寫專門的“屬性宏庫”,借用Rust編譯器的一部分,完成宏代碼的轉(zhuǎn)化和輸出。
過程宏是基于語法樹的分析過程,借助“樹”的結(jié)構(gòu)我們能理解它的一些特點(diǎn)。因?yàn)镽ust語法樹的子樹也是Rust代碼,所以宏的定義內(nèi)也可以完成語法分析,這就為代碼編輯器的提示和補(bǔ)全提供了便利。一個(gè)語法項(xiàng)目不可能同時(shí)屬于兩顆不是親子關(guān)系的子樹,因?yàn)槿绻麑儆趦深w子樹,將和語法樹的樹根產(chǎn)生環(huán),就和語法樹的定義相違背,所以語法項(xiàng)目都是獨(dú)立的,宏內(nèi)代碼的解析不會影響外界代碼的解析。
這樣的獨(dú)立性也就是“衛(wèi)生宏”思想的提出,Rust的過程宏可以理解為代碼的“內(nèi)部展開”,不影響代碼的上下文。正因?yàn)镽ust過程宏產(chǎn)生完整的語法子樹,它的定義不需要額外的界符,因此只需要滿足Rust語法就可以了。
在過程宏的定義之外,Rust語言提供了大量便于嵌入式開發(fā)的標(biāo)簽?!癮lign”標(biāo)簽定義內(nèi)存對齊的方式,“l(fā)ink_section”標(biāo)簽給定代碼要鏈接到的段或區(qū)。這樣,過程宏可以包裝各種各樣的標(biāo)簽,Rust語言的用戶可以方便地使用,而不需要深入宏了解代碼的具體要求。Rust語言定義的過程宏可以導(dǎo)出到包外,給其它的庫使用,這有利于嵌入式Rust生態(tài)的搭建和共享。Rust語言宏靈活的特性,讓宏在更多的領(lǐng)域有可用之處,更好地服務(wù)嵌入式平臺的開發(fā)工作。
嵌入式中的模塊化編程
Rust語言擁有很好的模塊化編程概念。傳統(tǒng)平臺的Rust語言中,社區(qū)總結(jié)出了“模塊-包-項(xiàng)目”的模型。這個(gè)模型也適用于嵌入式平臺,增加協(xié)作開發(fā)的效率,更好地共享生態(tài)。Rust的模塊化編程分為模塊、包、項(xiàng)目三級。模塊是Rust語言可見性分劃的最小單位,語言中提供了專門的關(guān)鍵字,來區(qū)分不同模塊的代碼和可見性,是由Rust語言本身確定的。在Rust語法中,“mod”是定義模塊的關(guān)鍵字,“pub”是定義可見性的關(guān)鍵字。
包是Rust項(xiàng)目的二進(jìn)制目標(biāo),這個(gè)等級是由Rust工具鏈給定的。每個(gè)包有版本號、作者和許可協(xié)議等元數(shù)據(jù),要依賴和使用的庫也要登記到包中,以便共同編譯。庫的特性有點(diǎn)像傳統(tǒng)語言的條件編譯,也是以包為單位規(guī)定的,每個(gè)包使用的庫可以開啟不同的特性,但庫在同一個(gè)包中開啟的特性是相同的。
“項(xiàng)目”這一層并非由Rust語言給定;人們開發(fā)軟件時(shí),發(fā)現(xiàn)一個(gè)解決方案中包含多個(gè)二進(jìn)制目標(biāo)是非常好的,總結(jié)之后就出現(xiàn)了項(xiàng)目的抽象模型。項(xiàng)目由核心和外圍包組成,或者是功能相近的一組包,它通常由同一個(gè)團(tuán)隊(duì)組織和維護(hù),可以在項(xiàng)目上添加擴(kuò)展。項(xiàng)目在習(xí)慣上由核心包到功能包,以依賴的形式構(gòu)成。實(shí)踐中,“項(xiàng)目”可以放在同一個(gè)工作空間里,以統(tǒng)一管理和發(fā)布編譯版本。
Rust將模塊化編程引入到嵌入式開發(fā)中,也可以方便地編寫測試和性能檢測代碼。模塊化編程能提高Rust嵌入式開發(fā)者的工作效率,適應(yīng)現(xiàn)代化嵌入式軟件的需求。
搭建Rust嵌入式生態(tài)
生態(tài)是軟件工業(yè)不可或缺的一部分。從編譯器到軟件支持,嵌入式Rust目前已經(jīng)擁有良好的基礎(chǔ)生態(tài)。此外,操作系統(tǒng)內(nèi)核也是嵌入式編程的重要部分,嵌入式Rust和內(nèi)核開發(fā)也有較好的相容度。你的架構(gòu)和指令集
嵌入式Rust的應(yīng)用支持分為兩個(gè)部分:一個(gè)是目標(biāo)處理核的支持,一個(gè)是芯片外設(shè)的支持。針對目標(biāo)處理核,首先我們要編譯Rust到這個(gè)指令集架構(gòu)。Rust語言提供豐富的編譯目標(biāo),主流的編譯目標(biāo)都有很好的支持;此外,如果有自主研發(fā)的指令集架構(gòu),可以為Rust添加自己的編譯目標(biāo)。編譯完成后,還需要編寫微架構(gòu)支持庫和微架構(gòu)運(yùn)行時(shí)。微架構(gòu)運(yùn)行時(shí)提供最小的啟動代碼實(shí)現(xiàn),能搭建一個(gè)適合Rust代碼運(yùn)行的環(huán)境。微架構(gòu)支持庫簡單包裝匯編代碼,允許應(yīng)用代碼操作寄存器、運(yùn)行特殊的指令,作為編譯器系統(tǒng)的補(bǔ)充。這之后,Rust對這個(gè)指令集架構(gòu)的代碼運(yùn)行支持就完成了。
嵌入式應(yīng)用定義了各有特點(diǎn)的中斷控制器,有些是指令集架構(gòu)定義的,有些是芯片設(shè)計(jì)廠家自己定義的。嵌入式Rust要支持這些中斷控制器,需要在微架構(gòu)運(yùn)行時(shí)中添加處理和封裝部分,或者作為通用架構(gòu)的補(bǔ)充,在專用架構(gòu)的支持庫中添加專有架構(gòu)的中斷運(yùn)行時(shí)。架構(gòu)雖然定義了標(biāo)準(zhǔn),但基地址、中斷數(shù)量等配置可能相互不同。這些元數(shù)據(jù)配置可以放在外設(shè)訪問庫的中斷部分,和架構(gòu)支持庫共同構(gòu)成中斷控制器的支持。
目標(biāo)的處理核定義了調(diào)試接口和閃存燒寫算法,我們需要在調(diào)試器軟件中編寫這些算法。社區(qū)通用的軟件“probe-rs”是很好的調(diào)試器實(shí)現(xiàn),可以替代OpenOCD,作為非常好的Rust語言調(diào)試軟件。如果自己的操作系統(tǒng)有軟件調(diào)試接口,可以添加操作系統(tǒng)調(diào)試器的載荷,共同完成調(diào)試軟件的部分。只要處理器廠商實(shí)現(xiàn)了調(diào)試接口,提供相關(guān)的文檔,配套的Rust軟件可以盡快完成,方便各種技術(shù)的開發(fā)者調(diào)試和使用。
嵌入式生態(tài)的標(biāo)準(zhǔn)
起初嵌入式開發(fā)者會為每個(gè)芯片都編寫一次代碼。隨著生態(tài)的發(fā)展,大家認(rèn)識到,需要提供一個(gè)基本的抽象,大家都圍繞著抽象去編寫,就能省下為大量外設(shè)反復(fù)編碼的時(shí)間。embedded-hal就是這樣的標(biāo)準(zhǔn),它是Rust語言的嵌入式外設(shè)抽象,支持大量的片內(nèi)和片外外設(shè),包括傳感器等,很好地?cái)U(kuò)充了嵌入式的生態(tài)。embedded-hal是統(tǒng)一的Rust語言標(biāo)準(zhǔn),它是針對外設(shè)功能本身的抽象,是抽象的集合,具體實(shí)現(xiàn)由實(shí)現(xiàn)庫去完成。它的擴(kuò)展性很好,比如“SPI-GPIO擴(kuò)展器”外設(shè)輸入SPI接口抽象,輸出GPIO的抽象,很多模塊都是抽象到抽象的過程,就可以方便的極聯(lián)、銜接和嵌套,整合更多的項(xiàng)目;這就非常容易為新的芯片編寫支持庫。
市場上海量的芯片都支持embedded-hal標(biāo)準(zhǔn)。K210、GD32V和BL602系列的芯片都提供很好的embedded-hal實(shí)現(xiàn)庫。要編寫embedded-hal標(biāo)準(zhǔn)的支持庫,只需要機(jī)器生成外設(shè)庫,然后編寫中間層庫,就能完成對此標(biāo)準(zhǔn)的原廠支持。
Rust與操作系統(tǒng)內(nèi)核
操作系統(tǒng)也是嵌入式應(yīng)用。常見的操作系統(tǒng)如按是否包含虛擬內(nèi)存區(qū)分,有不含虛擬內(nèi)存的實(shí)時(shí)系統(tǒng),和包含虛擬內(nèi)存?zhèn)鹘y(tǒng)操作系統(tǒng)?;谖⒓軜?gòu)的支持庫和運(yùn)行時(shí)庫,操作系統(tǒng)內(nèi)核可以很方便地編寫。社區(qū)中提供了大量成熟的操作系統(tǒng)運(yùn)行時(shí)。如rCore系列操作系統(tǒng)是第一個(gè)基于RISC-V架構(gòu)的完整Rust操作系統(tǒng),尤其適合教學(xué)使用。RTIC框架是中斷驅(qū)動的異步實(shí)時(shí)系統(tǒng),完全針對應(yīng)用使用Rust的宏語法生成,擁有極高的效率。Tock系統(tǒng)是針對微處理器的安全實(shí)時(shí)系統(tǒng),已經(jīng)用于手表、智能路標(biāo)和加密狗等產(chǎn)品。
針對操作系統(tǒng)和應(yīng)用程序開發(fā),Rust是適合編寫硬件驅(qū)動的語言。如果使用有產(chǎn)權(quán)的代碼,可以以混合鏈接的形式,與Rust代碼聯(lián)合編譯為二進(jìn)制使用。系統(tǒng)模塊、插件和動態(tài)鏈接庫等等都能受益于Rust語言內(nèi)存安全的特性,適合現(xiàn)在對安全敏感的開發(fā)需求。
物聯(lián)網(wǎng)系統(tǒng)要求嵌入式的操作系統(tǒng)能夠連上網(wǎng)絡(luò)。Rust嵌入式社區(qū)也在探索射頻連接的技術(shù)標(biāo)準(zhǔn),包括藍(lán)牙、WiFi等硬件標(biāo)準(zhǔn)。smoltcp是社區(qū)提供的非常好的TCP協(xié)議棧實(shí)現(xiàn),它可以代替lwip,在嵌入式系統(tǒng)領(lǐng)域高效、安全地完成網(wǎng)絡(luò)傳輸。搭配緩沖區(qū)和協(xié)議庫,物聯(lián)網(wǎng)操作系統(tǒng)就可以連上網(wǎng)了。
RustSBI:新型操作系統(tǒng)引導(dǎo)軟件
我們在開發(fā)操作系統(tǒng)內(nèi)核時(shí),有的內(nèi)核直接運(yùn)行在裸機(jī)上,有的還依托于一個(gè)運(yùn)行環(huán)境。在RISC-V上,“SBI”就是這樣的運(yùn)行環(huán)境。它除了引導(dǎo)啟動內(nèi)核,還將常駐后臺,提供操作系統(tǒng)需要的實(shí)用功能。RISC-V標(biāo)準(zhǔn)中,“SBI”意味著“操作系統(tǒng)二進(jìn)制接口”,運(yùn)行在其上的操作系統(tǒng)會通過環(huán)境調(diào)用“ecall”指令,陷入到二進(jìn)制接口的實(shí)現(xiàn)中,由其調(diào)用具體硬件的實(shí)現(xiàn)功能。這種實(shí)現(xiàn)被稱作“SBI實(shí)現(xiàn)”,社區(qū)常用的實(shí)現(xiàn)有開源的OpenSBI。RustSBI是鵬城實(shí)驗(yàn)室“rCore代碼之夏-2020”活動提出的SBI實(shí)現(xiàn),它是全新的操作系統(tǒng)引導(dǎo)軟件。
實(shí)現(xiàn)與模塊組成
RustSBI由幾個(gè)功能模塊組成。硬件環(huán)境接口實(shí)現(xiàn)了RISC-V SBI v0.2版本的接口,能運(yùn)行支持此版本的操作系統(tǒng)。硬件運(yùn)行時(shí)則是SBI實(shí)現(xiàn)運(yùn)行在裸機(jī)環(huán)境的必要模塊,它將由硬件啟動,開始運(yùn)行所有的RustSBI模塊。SBI的初始化完成后,將進(jìn)入引導(dǎo)啟動模塊,這里將發(fā)揮SBI標(biāo)準(zhǔn)“引導(dǎo)啟動”的功能,最終啟動操作系統(tǒng)內(nèi)核。另外,兼容性模塊能完成硬件到硬件間的支持,能模擬舊版硬件不存在的指令、寄存器,進(jìn)一步延長操作系統(tǒng)的生命周期。去年12月,RustSBI的0.1版本在深圳的Rust中國社區(qū)2020年年會上發(fā)布。使用目前最新的0.1.1版本,RustSBI已經(jīng)支持大量SBI標(biāo)準(zhǔn)提出的功能,支持大量自定義的擴(kuò)展功能;完全使用安全的Rust語言編寫,提高開發(fā)效率。開發(fā)Rust語言的操作系統(tǒng)內(nèi)核,可以統(tǒng)一編譯工具鏈。另外,RustSBI已經(jīng)被RISC-V組織收錄入RISC-V SBI標(biāo)準(zhǔn),它的實(shí)現(xiàn)編號為4。
RustSBI是一個(gè)庫,它以庫的形式設(shè)計(jì)的初衷是,便于平臺開發(fā)者“積木”式地引入庫的模塊,為自己的硬件目標(biāo)開發(fā)SBI支持。雖然RustSBI提供了QEMU、K210平臺的參考實(shí)現(xiàn),但應(yīng)用開發(fā)者不應(yīng)當(dāng)將自己的目標(biāo)也加入?yún)⒖紝?shí)現(xiàn)中,而是在自己的倉庫里引用RustSBI的模塊,可以選擇參考這些實(shí)現(xiàn)的內(nèi)容,最終完成完全可控的開發(fā)過程。這兩個(gè)平臺的使用范圍較廣,參考實(shí)現(xiàn)也會長期維護(hù),以發(fā)現(xiàn)RustSBI本身可能的少量問題,并及時(shí)修補(bǔ)完善。
為什么用Rust開發(fā)RustSBI呢?我們認(rèn)為,相比使用C語言,嵌入式Rust的生態(tài)圈在協(xié)調(diào)發(fā)展階段,它容易支持新硬件,Rust語言較強(qiáng)的編譯約束也提高了硬件代碼的安全性。
硬件到硬件的兼容性
RISC-V是快速更迭的指令集規(guī)范。我們?yōu)樾掳鍾ISC-V硬件編寫軟件,會遇到與舊版硬件不兼容的情況。硬件和硬件之間的兼容性,也能通過軟件完成——這是RustSBI提供的功能與亮點(diǎn)之一。RustSBI實(shí)現(xiàn)的硬件兼容性,是靠捕獲指令異常完成的。例如,K210平臺實(shí)現(xiàn)的是1.9.1版本的RISC-V特權(quán)級標(biāo)準(zhǔn),它規(guī)定了舊版的頁表刷新指令;而目前最新的1.11版標(biāo)準(zhǔn),規(guī)定的是新版的刷新指令。為新標(biāo)準(zhǔn)編寫的操作系統(tǒng)內(nèi)核,使用新版刷新指令,會因?yàn)镵210硬件無法找到新版指令,拋出非法指令異常。這個(gè)非法指令異常被RustSBI捕獲,它解析后,發(fā)現(xiàn)是新版的頁表刷新指令,便直接在硬件上運(yùn)行舊版的指令,完成指令的頁表刷新功能。
這種硬件兼容性,目前能支持新增的指令和寄存器。一切情況下,指令、寄存器在仍然存在,但新版中修改了它們的功能和意義。只靠RustSBI軟件本身,就不足以提供兼容性支持了。如果RISC-V芯片實(shí)現(xiàn)提供特定的兼容性外設(shè),比如這個(gè)外設(shè)能攔截特定CSR寄存器的訪問指令,就可以在功能修改的寄存器訪問時(shí),產(chǎn)生一個(gè)可供軟件捕獲的中斷。這樣的外設(shè)設(shè)計(jì)之后,使用RustSBI軟件,將能支持功能修改的指令和寄存器,將進(jìn)一步提升操作系統(tǒng)內(nèi)核的硬件兼容性。
兼容舊硬件,也是兼容未來新硬件的過程。未來的RISC-V標(biāo)準(zhǔn)快速發(fā)展,將與目前的硬件標(biāo)準(zhǔn)產(chǎn)生一定的差異;在硬件不變的前提下,未來軟件能對當(dāng)前的硬件兼容,就能延長軟件的生命周期。或許,我們未來升級RISC-V上的操作系統(tǒng),只需要更換硬件中的RustSBI固件,就能完美兼容最新標(biāo)準(zhǔn)的操作系統(tǒng)了。升級原有系統(tǒng)的硬件也非常容易,替換RustSBI固件就能達(dá)到升級效果。
另外,硬件兼容性也意味著實(shí)現(xiàn)硬件上缺少的指令集。當(dāng)這些指令集運(yùn)行時(shí),就會陷入到軟件中,由RustSBI軟件模擬這些指令,最終返回,這個(gè)過程應(yīng)用軟件不會有感知。當(dāng)然,這種軟件模擬過程可以滿足正確性,效率不如新版的硬件,但臨時(shí)運(yùn)行一個(gè)新版的軟件、體驗(yàn)新版的指令集還是足夠的。當(dāng)模擬指令的過程多到影響性能時(shí),也就是硬件該升級的時(shí)候了。
RustSBI與嵌入式Rust生態(tài)在RustSBI的實(shí)現(xiàn)中,多次使用“embedded-hal”的實(shí)現(xiàn)完成編寫過程。“embedded-hal”是Rust嵌入式的外設(shè)規(guī)范,它對大量廠家的外設(shè)提供了軟件支持。只要廠家的硬件支持“embedded-hal”,只需要編寫部分抽象接口代碼,RustSBI支持就可以快速地開發(fā)完成。
硬件處理核和SoC系統(tǒng)的開發(fā)也受益于設(shè)計(jì)好的RustSBI軟件架構(gòu)?!癛ustSBI很快速地實(shí)現(xiàn)了仿真環(huán)境的雙核測試,”華中科技大學(xué)的社區(qū)貢獻(xiàn)者車春池說,“這能為處理核提供豐富的測試環(huán)境,在開發(fā)高性能RISC-V處理核中非常重要?!?br>
無論硬件和軟件,我們都樂于看到各個(gè)應(yīng)用領(lǐng)域積極互動,嵌入式Rust生態(tài)的發(fā)展過程得到加快。“embedded-hal”本是裸機(jī)外設(shè)的標(biāo)準(zhǔn),RustSBI將這個(gè)標(biāo)準(zhǔn)運(yùn)用在引導(dǎo)軟件上,能加速裸機(jī)外設(shè)的開發(fā)和建設(shè),也能更快適配SBI標(biāo)準(zhǔn)到平臺上。
借這個(gè)項(xiàng)目,我們很高興能參與嵌入式領(lǐng)域Rust語言的建設(shè),希望這些微小的技術(shù)更新和迭代,最終能回饋到未來物聯(lián)網(wǎng)行業(yè)更輕便、更安全的開發(fā)體驗(yàn)中去。免責(zé)聲明:本文素材來源網(wǎng)絡(luò),版權(quán)歸原作者所有。如涉及作品版權(quán)問題,請與我聯(lián)系刪除。???????????????? ?END ?????????????????關(guān)注我的微信公眾號,回復(fù)“加群”按規(guī)則加入技術(shù)交流群。