*********************************************
セッションS4-a
テーマ:Rustは組込みシステムの歯車になれるか?
講師 :中林 智之 氏(Nature株式会社)
井田 健太 氏(株式会社フィックスターズ)
日時 :2021/9/3 13:40~14:50
参加人数:25人(開始時)
*********************************************
○ 自己紹介
中林智之氏
・Nature株式会社に所属
・ファームウェアエンジニア
・『組込みRust』の基礎部分を担当
井田健太氏
・株式会社フィックスターズに所属
・FPGAエンジニア
・『組込みRust』のアプリとAppendixを担当
○ Rust利用状況アンケート
・噂で聞いたことがある 40%
・入門済み 25%
・『組込みRust』購入済み 35%
・我こそはRustマスターである 0%
○ アジェンダ
・Rust概要
・組込みRust basic
・組込みRust advanced
・組込みRust事例
○ 大事なこと
Rustを推進することはC/C++を否定することではなく,組込みRustはC/C++を置き換えられるかはわからないし今すぐRustを使うことで全ての人が幸せになるとは限らない.
しかし,Rustは良い言語で書いていても楽しく,組込みの仕事でも使いたいと思っているため,今回のセッションでRustに興味を持ってくれれば,使ってみていただきたい.
○ Rustの概要
2015年にver.1.0がリリースされたばかりの言語で,OSやミドルウェアなどのシステムプログラミングで使えるように設計されており,
昨今では組込みへの活用も期待されている.
また,安全性とパフォーマンスを両立している言語で,2021年らしい言語の要素をバランス良く採用している.
Rustの雰囲気としては,手続き型のように書けば手続き型のようなプログラムになるし,関数型のように書けば関数型のようなプログラムになる.
採用事例としては,firefoxのwebエンジンや,RustでOSを書くプロジェクトとしてRedox,暗号化ライブラリなど,
Rustが得意な安全性を高める必要のあるところで採用事例が増えている.
Rustの特徴として,パフォーマンス,信頼性,生産性の3つがあり,さらにunsafeな操作ができる.
パフォーマンスの面でいうとC/C++と同等の速度であり,その理由として機械語を出力するコンパイル型であったり,
ガベージコレクションなしでのメモリ管理やゼロコスト抽象化が挙げられる.
また,unsafeを使った人力最適化が可能である点もパフォーマンスに寄与している.
信頼性の高い/安全なプログラミング言語とは,バグがゼロになるという意味ではない.
プログラミング言語のレベルでは型安全,メモリ安全であるというところが重要となっており,雑に言うとコンパイルが通ればわけのわからない動作をしない,ということである.
例えばC/C++では,バッファオーバーランやNULLポインタのデリファレンス,複数のスレッドからの同じデータへの読み書きなどがわけのわからない動作をしてしまう原因となる.
Rustでは,コンパイラが自動でチェックするためにわけのわからない動作が起こらないことが保証される.
所有権システムは実行時オーバーヘッドなしにダンブリングポインタをつくらないことと,並行処理時のデータ競合を防ぐことが,所有権システムの利点である.
また,ほとんどの場合にメモリリークが発生しないという利点や,オブジェクト同士の関係がぐちゃぐちゃになりにくいということもある.
ここからは,所有権システムをふわっとわかるように説明する.
所有権システムは,所有権,借用,ライフタイムからなる.問題を振り返ると,値を複数からアクセスできる場合,複数の変数から同じ値が変更されてしまい,データ競合が発生する.
また,どこで値を破棄したか,していいかわからないという問題がある.
所有権の考え方としては,全ての値に対して唯一の所有者がいるというものである.値を所有している所有者がスコープから外れると値は破棄され,値にアクセスできなくなる.
→不正なメモリへのアクセスが発生せず,自動的に値が破棄されるのでメモリリークが起きづらい.
ある値に対して所有者が唯一であることを保証するためにムーブという概念がある.
整数型のような型では値とともに所有権をコピーすれば良い.
しかし,データの大きい型であるとオーバーヘッドがかかってしまうため,値はコピーせず,所有権だけがムーブする.
所有権だけでは,値の「共有」ができず,大きなオブジェクトの関数の引数渡しなどで不便であるため,借用という概念が存在する.
借用は,"&"をつけることで,値の参照が得られるというものである.
→唯一の所有者というルールが崩れるため,Shared XOR Mutableという借用ルールの範囲内でのみ,参照できるようにしている.
値を借用することで安全に値を共有することができるが,値がこっそり破棄されていたりはしないのか?
→そこで,ライフタイムがある
ライフタイムとは値が有効かどうかをコンパイラが検証する仕組みであり,参照がスコープを抜けた変数に対してアクセスするとコンパイルエラーとなる.
所有権システムのおさらい
・所有権とは値の所有者を唯一にし,データ競合を防ぎ,所有者がスコープを外れると値を破棄する仕組み
・所有権だけでは値の共有ができないため,借用という値を安全に共有する仕組みを用いる
・借用を用いると共有した値が生きているかどうかわからなくなるため,ライフタイムという仕組みを用いる
・これらの3つを用いてダングリングポインタを作らないなどの問題を解決しているのが,所有権システムとなる
時間の関係で生産性については割愛する.
Rustコンパイラの安全保障外の操作をするときは,unsafeを用いる.unsafeを用いることでハードウェアを制御したり,C言語の関数を呼び出したりできるようになっている.
Rustは学習が大変なのか?
→Yes.システムプログラミングが可能であり,近代的な言語機能を取り込んでいるので,そのようなバックグラウンドがないと大変である.
しかし,所有権システムが困難な問題を解決しているため,払わなければいけないコストよりは恩恵が勝るのではと考えている.
○ 組込みRust basic
ここでは組込みRustとはどういうものなのか,ベアメタルのRustの環境について,組込みで活きるRustの機能を紹介する.
組込みRustとは,組込みシステム開発でRustを使うことである.広義では組込みLinuxなどを含むが,本発表では主にOSなしでのマイコン環境をターゲットとしている.
組込みLinuxに関して情報を共有しておくと,ユーザランドアプリケーション開発は普通に使えるが,kernel/device driverについてはまだ議論中である.
なぜRustで組込みなのか?
→パフォーマンス,信頼性,生産性と,組込み開発に必要な要件を全部満たしている.
また,日本語の本が増えてきており,遊べる環境が整ってきている.
個人的なモチベーションとして,C/C++以外にも選択肢があってもいいのではないか,C言語が組込みの主流でなくなる世界を見たいというのがある.
組込みRustだとunsafeだらけになって旨味がないのではないか?
→ハードウェアを制御する部分は何の言語を使っても同じ状況であるが,unsafeなレイヤの上にsafeなレイヤを作ることでRustを使うことによる安全性が担保される.
no_std
→Rustでベアメタルプログラミングをする上でのキーワード
Rustの標準ライブラリはcore,alloc,stdの3レベル構成である.stdはOSが前提,allocはメモリアロケータが必要であるが,coreは無条件で使えるようになっている.
→stdがない部分の環境のことを,no_stdと呼ぶ.
no_stdで使えるcrateがあったり,gdbが使えるため,no_stdでも意外となんとかなる.また,VxWorksやESP-IDFなど組込み環境でstdを整備する流れもある.
組込みで活きるRustの機能として型状態プログラミングを紹介する.型状態プログラミングとは,Rustの強力な型システムを利用して状態を型で表現することである.
通常の状態マシンでは実行時にコストがかかるが,状態遷移をある型から別の型への変換と捉えてコンパイル時に解決することでコストなしで状態遷移を行う.
→操作ミスをコンパイル時に防げる.(例:初期化状態でのGPIO出力レベルを変更するメソッド呼び出しはコンパイルエラー)
現在の組込みで使えるフレームワークやOSとしては,embedded-halやTock,DroneOSなどがある.
--- 講師交代 ---
○ 組込みRust advanced
ここではひとつだけトピックを紹介する.
Rustでは既存資産を使えるのかという質問がある.
全てRustで記述するだけのリソースがあればよいが現実的ではない.
→Cとの相互運用が必要
C FFIという形で,RustとCがお互いに関数を呼び出せる.
CからRustを呼び出す場合
→#[no_mangle]属性とextern "C"を関数につけることでCからRustの関数を呼び出せる.
RustからCの関数を呼び出す場合もextern "C"をつけることで可能になる.
C言語とのプリミティブ型との対応はどうするのか?
・RustとCでは直接対応する型がない
・Rustの対応する型を明示する必要がある
→プラットフォームごとに定義を変えるなどすることが必要であり,面倒であるため,ctyクレートを用いることで,Cのプリミティブ型に対応するRustの型を定義できる.
構造体の変換はどうするのか?
→Cの構造体を扱う場合,同等の構造を持つRustでの定義が必要になる.その際,構造体のフィールドの配置規則をCと同等にするために#[repr(C)]属性をつけて定義する.
しかし,手動でRustからCの関数を呼び出すための定義を定義するのは時間がかかる.
bindgenを用いて自動で定義を生成できる.bindgenの雑感として,bindgenのオプション調整は必要だが,手書きよりははるかに楽である.
組込みシステムでの現実的な利用法として,
・C FFIを利用しつつ,アプリケーションの本体など重要な部分から徐々に置き換える運用も可能である.
・bindgenなどのツールを利用すれば,現実的なコストでCの既存資産を呼び出し可能である.
○ 組込みRustの活用事例
● CMSIS-DAPの話
Arm Cortex系のデバッグ機能にUSB経由でアクセスするための仕様とファームウェア実装である.CMSIS-DAPに準拠するとデバッグ用のアダプタが作れる.
組込みRustでも用いているWio TerminalのデバッグではSeeeduino XIAOを使用し,組込みRust執筆時は既存のファームウェアを用いている.
→既存ファームウェアはWindowsのドライバが自動インストールできない.
→RustでCMSIS-DAPを実装すればよいのでは.
→Seeduino XIAOのファームウェアをRustで書くことができ,USBも扱うことができるため,実装は可能である.
開発の流れとしては,USBデバイスとしての機能を実装していき,おおよそ3日くらいで実装が完了した.
→USBデバイスの開発としてはかなり楽であった.
実装してみて感じたRustの便利なところとして、
・cargoが便利
・ハードウェア抽象化がよくできている
・Result型によりエラー伝播が楽にかける
・範囲外アクセスが未定義動作とならない
という点が挙げられる.
結局のところ,現代的な仕様の言語,組込みで使える要求コストの低さを両立しているところが便利である.
--- 講師交代 ---
● ECHONET Liteのライブラリを書いてみた
Nature株式会社ではNature Remo E/E Liteという家庭内エネルギーマネジメントシステムの製品を作っている.
各機器とはECHONET Liteというスマートホームを実現する規格で通信している.
開発経緯
多数のECHONET Lite機器が存在しているおり,実験設備を求めて全国行脚していた際,もっと便利なツールが欲しいということで,勢いでRustで書いてみようとなった.
echonet-lite-rsがcrates.ioで公開されている.
→ECHONET LiteパケットのSerializer/Deserializerが実装されており,UDPで通信する部分は別途必要である.
ファームウェア開発にRustで開発する戦略として,フルスクラッチではなく一部をRustで置き換えていく戦略を考えている.
→まずは便利ツールとして使ってもらい,便利ツールからの利用で完成度をあげ,いざ新しい機器を開発するときに完成品としてRustをこっそり入れる.
echonet-lite-rsの活用事例
・ECHONET Lite機器のスキャナー
・ECHONET Lite機器エミュレータ
echonet-lite-rsを試すには?
リポジトリのサンプルプログラムよりLAN内のECHONET Lite機器から動作状態を取得できる.
→しかし,逸般的な誤家庭にしかないという噂がある.
→家にNature Remo E/E Liteがあれば良いので,使ってみていただければ幸い.
Rustを使ってよかったこと
・雑に書いても(正しく)動く
・パフォーマンスを気にしないところはコピーを実装すれば所有権システムにあまり悩まされない
・コンパイルが通れば不安なく動く
・圧倒的に手戻りが少ない
・ライブラリが優秀
・serde,structoptが優秀である.
serde
→デファクトスタンダードのSerializer/Deserializerで,様々なフォーマットに対応している.
structopt
→コマンドラインオプションをいい感じに作れる.
no_stdの困りごと
・ライブラリ機能が制限される
・まだ定番の方法が確立されていない
組込みRustをおすすめできる人
・パフォーマンスを追求したい人
・コンパイラにできるだけたくさんチェックしてもらいたい人
・雑に書いても正しく動いてそれなりに性能を出したい人
・型システムでパズルを楽しみたい人
質疑応答
I氏「ECHONET Liteのソフトは組込みではないのではないか」
回答:一部(パケットの変換する部分)は組込みの環境でも使えるように作ってある.
M氏「本来の仕様が意図していないが,ユーザがなんとなく同じ動きをするからいいやといった間違いが生まれやすいかそうでないか」
回答:Rustで正しく動くというのは,未定義動作を許さないというところになるので,そもそも仕様がおかしいものはRustであってもどうしようもならない.
まとめ
Rustは組込みシステムの歯車になれるか?
→ポテンシャルはある!最後はマンパワー,人口を増やさないとどうしようもない
組込みRustを始めるのに最高の本が組込みRust