iT邦幫忙

2021 iThome 鐵人賽

DAY 1
1
Software Development

予焦啦!Hoddarla 專案起步:使用 Golang 撰寫 RISC-V 作業系統的初步探索系列 第 1

予焦啦!目錄、快速上手與前言

目錄

快速上手

予焦啦!歡迎各位讀者點進這個系列文!

第一天,我們不囉唆直接去拉取 Hoddarla 專案吧!

筆者建議讀者跟隨快速上手章節的部份就好,應該可以直接跑出一個具備計時器中斷(timer interrupt)功能的小型展示。這個展示包含的內容大約涵蓋到第 20 天的內容,後續還在趕工中。現在的這個名為 debut 的分支,將目前為止的 Hoddarla 開發進度壓成了一整個 patch。之後隨著每一天的推進,筆者會依序揭露試誤與開發的過程。

git clone git@github.com:NonerKao/Hoddarla.git && cd Hoddarla
make env
. .hdlarc
make

理論上,應該可以看到

...
i = 16
i = 15
i = 14
i = 13
i = 12
i = 11
i = 10
i = 9
i = 8
i = 7
i = 6
i = 5
i = 4
i = 3
i = 2
i = 1
i = 16
i = 15
i = 14
i = 13
i = 12
i = 11

的一連串倒數。是的,這就是囉!

建置開發環境的步驟展開

以下是 make env 較細部的拆解,但實務上應該是沒有必要的,只是參照用。

模擬器使用 QEMU

考量到 RISC-V 硬體還不是很好取得,開發者總還是必須要有一個可運行系統的平台才行,所以通常就使用功能最齊全的模擬器 QEMU。

筆者使用 QEMU 6.1.0 的版本。以下將幾個套件取消掉並非絕對,只是現階段還不需要那些功能罷了。--target-list 則是必要的,因為 QEMU 能夠支援多種平台與架構,這裡指定的 riscv64-softmmu 通常用以運行 Linux。prefix 雖非必要但仍然強烈建議,因為如此一來,就可以很方便的管理安裝目錄了。

./configure \
    --target-list=riscv64-softmmu \
    --disable-sdl \
    --disable-vnc \
    --disable-gtk \
    --prefix=$HOME/qemu
    # 可自行調整想安裝的目錄位址,建議將 $HOME/qemu/bin 也加入 PATH 環境變數

如果成功的話,會顯示一整排的組態,以及它們是否被啟動或取消。這之後就可以

make && make install

GNU C 工具鏈

目前 Hoddarla 還是需要仰賴 GNU 的 C 工具鏈。自己編的話需要花一點時間,如果可以使用發行版就有的軟體包,也是可以考慮的選項。

自行建置

最簡單的方法就是先拉取一份 riscv-gnu-toolchain 到自己的開發環境,然後開始建置

git clone https://github.com/riscv/riscv-gnu-toolchain
cd riscv-gnu-toolchain
git submodule update --init riscv-gcc
git submodule update --init riscv-newlib
git submodule update --init riscv-binutils
git submodule update --init riscv-gdb
mkdir build && cd build && ../configure --prefix=$HOME/toolchain
make

結束之後就會產生預設 64-bit 的 newlib 工具鏈。

預先建置/發行版打包版本

筆者沒有使用過這些版本。若要使用這類型的工具鏈於 Hoddarla 實驗或開發的話可能會因此需要一些調整。

  • Bootlin:這是一個提供開源服務的公司,點選之後在 Select arch 選擇 riscv64,glibc 和 musl 應該都可以使用。
  • Arch Linux 的 community/riscv64-elf-binutilscommunity/riscv64-elf-gdb:可以直接使用 pacman -S 安裝

前言

筆者在 2016年底首次參加ITHOME的鐵人賽。

該次系列文是非常淺碟的學習筆記,內容也沒有什麼特殊之處,是關於使用者空間與Linux作業系統核心之間的系統呼叫介面。那次的經驗讓我明顯感受到,鐵人賽本質上是一場猶如馬拉松的盛會;參賽者們位在其中,各自由意志力驅使自我成長,同時也有機會學習他人或供他人學習,整個活動空間於是成為多面向且多維度的集體學習場域。

撰寫系列文是很有趣,介紹性質或是學習性質的文章也可以幫助初學者,但是要更加提升的話,必定要往更深處走去吧。

於是在後來的兩年,2018第 11 屆 2020,筆者決定鎖定更具體的範圍、訂出明確的目標。前者是以 binutils 這樣偉大的專案為目標去試圖實作一部分子集合,後者則是追蹤 Golang 內建的各種機制。以結果來說,兩篇系列文最後還是流於通泛了,但它們確實標註了筆者過去幾年以來一直都很感興趣的的技術主軸:RISC-V 以及 Golang 。

開發之經緯

這一次,筆者決定開始一個以 Golang 與 RISC-V 兩個主題為雙軸線的作業系統專案,並將之命名為 Hoddarla。當然,要是說我打算在這 30 天內一天累積一點點的速度,完成一個作業系統,那未免也太瞧不起數十年來累積的作業系統原理與技術了。

事實上,在第十一屆(上上屆)鐵人賽結束之後,筆者就已經著手進行 Hoddarla 專案:使用 Golang 打造一個 RISC-V 的作業系統。到今年(寫作的此時,是 2021)初期累積了非常微小的一點點成果,遠遠不及當初的恢宏夢想,但還是以這次鐵人賽為契機紀錄一下;題目也相對應的修改成符合事實的描述:Hoddarla 專案起步:使用 Golang 撰寫 RISC-V 作業系統的初步探索

以下數個小節,就解析這個題目的若干名詞對筆者的意義之所在。希望在今天這一篇開宗明義的文章當中能夠指引各位讀者,通往後續幾篇技術文章的道路或許會更有趣一些。

到底為什麼是 RISC-V 與 Golang?

這兩個大項目純粹是筆者的興趣,也因此想要做的題目形式上會成為兩者的交集點,事實上兩者沒有什麼關聯。RISC-V 是眾所矚目的第五代簡化指令集架構,官方單位(RISC-V 國際基金會)以電腦系統的巴別塔為己任,以模組化的彈性為最大的賣點,技術社群與規格社群也相當蓬勃地發展當中。Golang 則是一個高階的靜態編譯程式語言,語法簡潔卻功能強大,由頗負盛名的Ken Thompson(Unix之父、C語言之父)、Rob Pike(UTF-8編碼設計者)以及Robert Griesemer開發。

若真的要定位出兩者的相同點,也不是沒有:

  • 從研究走向實用:RISC-V 起源自柏克萊大學的研究室,而 Golang 原先是為了解決軟體工程開發的基本問題的 Google 研究專案。
  • 由領域大師發起:Unix/C 語言之父與計算機組織經典教科書算盤本的教授 David Patterson 是專案的發起人。
  • 極簡主義:兩者同樣倡導「少即是多」的極簡哲學。Golang 三人小組在第一次召開會議之時,最先確立的共識就僅有「C++ 的複雜令人厭惡」的一點;而 RISC-V 則在白皮書中大量比較 x86 的複雜本質連帶影響軟體工具設計,以及 ARM,雖號稱是 RISC 卻仍然包含許多根本用不到的複雜指令。

或許還可以再補上一則筆者的個人主觀意見,那就是兩者都還沒有得到應有的地位。試想一個內建擴充性的計算機指令集架構(ISA),如果能夠讓全世界共享其中的基礎部分,能夠相消弭多少的額外負擔?又,一個語法極簡卻提供垃圾回收、輕量程序與內建並行機制的語言,可以為多少軟體工程師節省認知負荷?然而世界並非如此運作,這兩者能夠在歷史之中挺立多久,都尚屬未知。

至於兩者真正的交集,也就是 Golang 內的 RISC-V 架構支援,目前只有 Linux 作業系統堪稱較完整,但其實也還非常初階。僅有 64-bit 的支援不說,能夠大幅節省編譯產物空間的壓縮指令集(C extension)也還沒有支援。

若是各位讀者對於 Golang 與 RISC-V 之間的真正的交集有興趣,筆者曾提交兩個功能實作,都已經進入到 Golang 的歷史之中。相關主題會在最後倒數幾日時的附錄主題與各位分享。

無論如何,由於我完全認同這兩者背後的哲學,但又沒有非常充裕的時間能夠徹底研究這兩者,所以才會繼前兩組鐵人系列文之後,再於今年度的鐵人賽延續這個雙軸線。

工具鏈比較:C vs. Golang

我們可以從不同角度檢視一個程式語言:語法、語意如何處理、執行時環境(runtime)與行為,都有各自的學問與鑽研的趣味。

在這裡我特別想比較的是工具鏈(toolchain)的部份。C 語言工具鏈的編譯牽涉到多個軟體元件、多個專案的偕同工作才有辦法達成。若要簡單的描述 GNU C 工具鏈的編譯流程的話,那麼得先將鏈結器、組譯器之類的工具編譯出來,再以這些工具先編譯出一個功能有限的 C 編譯器;然後再用這個編譯器去編譯 C 函式庫,這時候才能讓 gcc 專案使用到 C 函式庫的功能並編譯成一個整體。Golang 的話,若非特殊情況,相依性管理完全不是問題,因為它自己為自己實作了編譯器,也就是所謂的自主(Self-Hosting)。

工具鏈本身編好之後,若要執行測試來檢驗它是否功能正常,C 語言需要外部的測試專案;但是,go 語言已將單元測試當作是語言的基本功能,也能夠在編譯好工具鏈之後自動執行所有標準函式庫的測試。

執行時間則是另外一個兩者相差懸殊的項目,我在同一台電腦上面實測的結果,C 語言工具鏈的下載與編譯時間都大概是 Golang 的 9 倍左右。

從使用者的角度來看的話,我覺得最大的一個差異在於交叉編譯(cross-compilation)的支援。C 語言部分,以 GNU 工具鏈為例的話,交叉編譯往往仰賴建置工具 autotool 系列所提供的功能;誠然,相關資訊在網路上都非常容易取得,也容易理解。但有些 C 語言為主的專案,它預設使用的建置系統並無 GNU 建置工具整合,那麼要交叉編譯就有可能要費一番手腳;又,就算有整合 GNU 建置工具,若是要支援新平台,專案本身和 GNU 建置工具裡面的部分設定檔可能都需要更新,這需要不特定且無法輕易估算的額外工夫。

各位讀者知道什麼叫做 Cannadian Build 嗎?這是在描述一種很有趣的交叉編譯需求。你最強力的運算機器是架構 A,但你預期會用到交叉編譯工具鏈的機器是架構 B,而這隻交叉編譯工具所能夠處理與生成的產物是屬於架構 C。我實測在 x86_64 上為 ARM64 編譯 RISC-V 需要的工具鏈,結果是寸步難行:最一開始,GNU 工具會提示你,這個編譯工作會需要 x86_64 到 ARM64 的交叉工具鏈,這就算了;就算你備有了,他接下來還會跟你要 ARM64 上面所需要的各種過程中的相依函式庫。

反觀 Golang,交叉編譯功能是內建的。Golang 專案需要的建置指令只是 go build,所以如果想要為 ARM 64 位元處理器上的 FreeBSD 系統建置這個專案,只需要多給兩個環境變數

GOOS=freebsd GOARCH=arm64 go build

旋即搞定。筆者甚至沒有辦法花更多篇幅來說明它,這個抽象層切得妙到顛毫,所有無須深入理解的細節全部都被隱藏。

由此,我們不難發現,Golang 作為一個語言,的確解決了一些困擾著前人的問題,而它解決那些問題的解法也確實的成為這個語言本身提供的工具。

本節舉這個工具鏈交叉編譯的例子,對於 Hoddarla 專案本身不是太具意義,因為我們已經限定 RISC-V 64-bit 了。也許圍繞在 Golang 上面更合適介紹的主題是 go mod 導入的內建相依性管理,但就留待日後有機會再行研究。

作業系統

Jserv 在八卦版的一篇文 曾引述王祐中博士的一句話:「寫一個作業系統是多麼美好的事,在有限的生命中千萬不要遺漏了它。」無獨有偶,這次鐵人賽也由另外一位參賽鐵人 EN 也引用了這句話在他打算要打造的 RISC-V 作業系統的系列文介紹當中。

若以人類的文明活動作為類比,作業系統負責的項目應該可以類比於政府體系、官僚制度或者是任何非現代政府形式的調解手段。原因是,作業系統負責管控資源,能夠監控它們的狀態且有最終決定權,然後接收使用者的需求並將這些資源分配給他們。這麼說來,將寫一個作業系統視作美好之事,大概略可以類比於那些想要設計社會制度的思想家,或是有才幹能夠在有限規則中將政府的運作調理得宜的政治家吧。

然而在電腦系統中,寫一個專案就意味著作者有著至高權限,比之皇帝或是史上任何的獨裁者都還具有更多的發揮空間,簡直就是神一般的存在。這麼一想,的確是令人嚮往的事情。

但如果只是不論結果的撰寫而已,感覺又略嫌缺乏。雖然人們常說意義在過程不在結果,但通常那要嘛是安慰人的話語,要嘛是結果不重要或是難以估量。筆者謙卑的希望 Hoddarla 能夠至少在過程的意義之外還能夠具備一些其他的意義,但也如同前述的那樣,今年能夠完成的部份實在是頂多只有初期的一部分,就算要高談闊論些精神層面的事情,也不是現在吧。

開發思路

Hoddarla 背後的想法很單純。筆者最一開始煩惱了很久,到底該將這個專案命名為什麼才能夠彰顯那種無須多言的簡單感覺,幸好傳統語言的智慧自然在對的時刻浮現腦海。

Golang 的執行期環境(runtime)提供了非常豐富的功能,包含排程、Goroutine(Golang 的 coroutine)、還有垃圾回收機制。單這樣描述,就已經很像是作業系統核心的部份內容了,不是嗎?當然,一個現代的作業系統核心包含了很多很多很多其他的功能,但從無到有不就是這麼回事嗎?那我們何不試試用 Golang 來寫作業系統呢?

需要特別注意的是,這裡面有個應當釐清的誤區。上一段的聯想實際上代表著,也許 Golang 的執行期環境能夠代替某些作業系統核心的功能,但這和用 Golang 寫作業系統的意義不盡相同,值得細分。

以 C 語言為例,用 C 語言寫 Linux 沒有什麼難以想像的,畢竟是已屆 30 年的實踐。然而,Linux 並不使用 C 語言的執行期環境。Linux 需要 bootloader 幫它處理載入時的核心參數,而不是像一般 C 語言專案那樣,由 C 語言的執行期處理參數,再以 int main(int argc, char *argv[]) 的方便形式傳遞。所以從 C 的例子就能夠明確看出兩者的差異。

那麼,Hoddarla 專案所冀求的又是哪一種呢?筆者認為在這個階段(這次鐵人賽所包含的範圍內),是只能涵蓋前者的一部分。至於用 Golang 寫作業系統,在附錄的部份會有一些文獻回顧,介紹前人的嘗試,並較深入一點地研究目前最完整的一個使用 Golang 撰寫的作業系統核心;關鍵字是 biscuit

因此,現階段 Hoddarla 的絕大部分程式碼都依附在 Golang 專案之中;甚至我們可以說,Hoddarla 目前還只是 Golang 的 RISC-V 執行期的一個小 patchset 而已。筆者在裡面發展現有 RISC-V 工具鏈不足之處,增補執行期所需,然後在執行期環境的啟動過程中一個一個加入作業系統應有的功能。

作為 patchset,筆者的例行公事就是每隔一段時間就要將既有的部份 rebase 到上游的 Golang 上面。Hoddarla 啟動至今已經這樣操作十數次,大部份是執行期環境的 API 改動,偶爾涉及到物件處理的部份而需要重新編譯工具鏈。繁瑣的細節在附錄中也將補述。

各路英雄,切磋於鐵人賽

晶心壯士團體第一次上台領獎時,記得主辦單位提供了隊長發表感言的時段。當時筆者秉持著感動與感謝,將這場盛會與馬拉松做聯想,以強調其中的耐力成份。現在鐵人賽規模更顯壯大了,除了自我的耐力考驗之外,如果還獨學而無友,顯然白白糟蹋了一場祭典。所以筆者這裡也列出相關主題,以示重點關注:

  • 微自幹的作業系統輕旅行:雖然說打造作業系統的目標相近,且目標架構也都一樣是 RISC-V,但這一系列的實踐手段看起來應該會與 Hoddarla 有很大的區隔。雖然只是推測,但我預期想要學習作業系統核心原理的讀者能夠從這個系列獲得較多,筆者的 Hoddarla 系列文已經趨於邪道了。
  • 從C到JS的同步非同步探索:這個系列探討非同步相關的主題,目前為止資訊相當深厚,且又旁徵博引。
  • [機派X] 無人機與樹莓派的相遇 Linux不只是過客:以 Linux 為關鍵字搜索,這篇系列看起來會更深入嵌入式系統的運用。
  • arm 還是 x86? 我該怎麼選呢:對於 RISC-V 來講,ARM 和 x86 這兩位老大哥都是遲早要趕過的目標。作者在介紹中提及會有效能的比較,讓人額外感興趣。

當然最後也一定要介紹一下敝組晶心壯士中的另外兩位鐵人。他們都是是筆者在晶心科技的同事。他們帶來的兩個系列將分別是

  • 閱讀 Linux Kernel 文件:將 Linux 核心源碼當中的文件打撈出來閱讀,並分享從中獲得的知識。這系列應該會相當適合在 Linux 核心開發稍有基礎的讀者。
  • Port Alpine Linux to open source RISC-V platform:將 Alpine 這個 Linux 發行版移植到開源的 RISC-V 32-bit 硬體平台上面。這系列顯然更能夠迎合中級甚至進階的嵌入式系統玩家。讀者將能夠從使用者以外的視角一探發行版內部的奧秘,並了解移植的實務如何進行。

小結

予焦啦!感謝各位讀者願意在這裡見證 Hoddarla 專案的起步。我們明天再會!


下一篇
予焦啦!準備工具鏈
系列文
予焦啦!Hoddarla 專案起步:使用 Golang 撰寫 RISC-V 作業系統的初步探索33

尚未有邦友留言

立即登入留言