Wasm 怎麼被執行的
體驗了 Wasm+Container 的執行結果後,感覺如何呢?是不是完全感受不到跟普通的 Container 的差距呢?
那!就!對!了!
Wasm Container 不是也不會取代傳統的 Container,相反的,可以想像他是一個協助者,幫助傳統 Container 補足不夠的地方。因此,Wasm Container 的終極目標便是讓開發者有感(因為製作 Container Image 時會需要編譯出 Wasm 格式),但使用者不會感受到任何差異。
在這個章節裡,將說明一下 Wasm 是怎麼被執行的,有了這個基礎概念會更容易理解 Docker / crun 是怎麼與 Wasm Container 進行互動的。
二進位格式(Binary Format)與文字格式(Text Format)
Wasm 在規格裡定義了兩種格式:
- 編譯與部署為主的二進位格式,副檔名為
.wasm
- 具備人類可讀性的文字格式,副檔名為
.wat
市面上的 Wasm Runtime 是以能執行二進位格式為主,然而有些 Runtime 也原生支援文字格式。由於 wat
與 wasm
是一個直接轉換的關係,若想進行這兩種格式的轉換,可以透過 wabt 工具鏈中的 wat2wasm
(將 wat 轉換成 wasm) 與 wasm2wat
(將 wasm 轉換成 wat)來達成。
由於介紹二進位格式內的指令等細節過於瑣碎,有興趣的讀者可以自行研讀 Wasm 規格書。
Wasm 的載入、驗證、與執行
本段落參考資料
當 Wasm Runtime 收到使用者的請求,準備執行一個 Wasm 應用程式時,會經過以下三個主要的階段。請注意,由於不同 Runtime 的實作,可能會在這個大框架上有些細節的差異,如在特定的階段收集或者預處理資料,以加速後續的執行效率等操作。
- 解碼階段(Decoding):由於部署的 Wasm 應用程式是以二進位格式所發佈的,因此第一個階段的任務便是把這個格式轉換成一個「模組(Module)」,在應用程式中的函式、全域變數等資料都包含在模組之中。由於接下來的兩個階段會針對這個模組進行驗證與執行,因此這個模組通常會是以各個 Runtime 自行定義的中介表示碼(Internal Representation)來儲存。
- 驗證階段(Validation):為了確保使用者提供的 Wasm 是合法的,在這個階段的任務是將被解碼後的模組進行數個符合規格書定義的語法檢查來確保該模組是安全且有意義的。這類的驗證包含但不限於針對函式與指令串的型別檢查、確保運算值堆疊(operand stack)是一致的等檢查。若連驗證階段都無法通過,自然就沒有執行的必要。就跟沒有完成的料理就沒有試吃的必要一樣(/ω\)
- 執行階段(Execution):就跟四大天王一定有五個人一樣,你以為執行階段就只有一個嗎?錯了,他有兩個↖(^ω^)↗
- 實例化階段(Instantiation):通過驗證的模組畢竟只是一個中介表示碼,需要經過實例化的階段來構建出模組實例(module instance),建構出該實例的狀態(state)與執行用的堆疊(execution stack)。因此,在此階段將進行全域變數、記憶實例等模組內的各個元件的初始化等。想當然不是這麼精簡的說明,請查閱規格書上非常非常多的細節。
- 執行階段(Invocation):模組中會定義輸出函式(exported functions,我不確定這個翻譯是否精準,若有知道的夥伴請留言讓我知道),在執行階段,Runtime 此時將使用者想呼叫的函式名稱與對應的參數傳遞進來,從輸出函式中找到同名的函式進行呼叫,並回傳結果。通常有
main
函式的應用程式,對應到 Wasm 中便是輸出函式 _start
。
因此,在 Wasm+Container 的範例中,Docker 最後觸發的就是該應用程式(/hello.wasm
)中的 _start
exported function。