Zxx⾔語によるRTOSの実現

SWEST22

修正第2版

Zig⾔語およびZen⾔語による
リアルタイムOSの実現
2020年8⽉21⽇

⾼⽥ 広章
NPO法⼈ TOPPERSプロジェクト 会⻑
名古屋⼤学 未来社会創造機構 モビリティ社会研究所 教授
名古屋⼤学 ⼤学院情報学研究科 教授
附属組込みシステム研究センター⻑
APTJ株式会社 代表取締役会⻑兼社⻑
Email: hiro@ertl.jp URL: http://www.ertl.jp/~hiro/

Hiroaki Takada

1

Zxx⾔語によるRTOSの実現

AGENDA
取り組みの背景
Zig言語とZen言語の紹介▶ プログラミング言語 Zig▶ プログラミング言語 Zen
Zig言語およびZen言語（Zxx言語）によるRTOSの実現▶ 実装の概要，ZxxによるRTOSの実装コード例▶ 静的APIとコンフィギュレータの置き換え▶ 実現したRTOSの性能評価▶ Zig言語およびZen言語の現時点での評価
プログラミング言語の安全性
今後の取り組みとソースコードへのアクセス［付録］ Zigの言語仕様のチラ見せ：ポインタとその仲間
Hiroaki Takada

2

Zxx⾔語によるRTOSの実現

取り組みの背景
組込みシステム開発技術の革新▶ 情報技術が進む中で，新しい技術を取り込んで，組込みシステム開発技術も革新させていくべき▶ 組込みシステム開発技術の “デジタルトランスフォー
メーション”▶ オープンソースソフトウェアは，新しい開発技術をトライア

ルするには良い題材▶ トライアルした成果をオープンに議論できる
問題意識：いつまでもC言語で良いのか？
▶ 多くの組込みシステム開発者が感じている問題意識▶ 新しいプログラミング言語は常に登場しており，いつが乗り換え時かは難しい
Hiroaki Takada

3

Zxx⾔語によるRTOSの実現

プログラミング⾔語 Zig☞ https://ziglang.org/
Zigとは？（Zigのウェブサイトより）▶ Zig is a general-purpose programming language andtoolchain for maintaining robust, optimal,
and reusable (+ maintainable) software.
言語の位置付け▶ C言語を現代風に改良・拡張したもの▶ OSカーネル，組込みシステム，リアルタイムシステムは想定されている用途
なぜZigを選んだか？
▶ 静的APIを置き換えられる可能性があると考えたため▶ コンパイル時のコード実行の機能▶ RTOSの記述に向いていると思われるため▶ すべての資源を明示的に管理できる
Hiroaki Takada

4

Zxx⾔語によるRTOSの実現

他の新しいプログラミング言語との位置付け▶ 組込みシステムにも向いた言語として，GoやRustなどが注目されているが…
▶ “Go is better Java/C#”▶ “Rust is better C++”▶ “Zig is better C”☞ https://kristoff.it/blog/why-go-and-not-rust/▶ 明確な違い：ヒープ領域の扱い▶ Go：ヒープ領域の使用が前提．領域の解放は，ガベー
ジコレクタ（GC）により行う▶ Rust：ヒープ領域の使用が前提．「所有権」の概念の導入により，解放するタイミングを静的に決定▶ Zig：ヒープ領域は必須ではない．使う場合には，明示的にアロケータを呼ぶ（複数のアロケータを持つことができ，どのアロケータを使うかも明示する）．解放も明示
Hiroaki Takada

5

Zxx⾔語によるRTOSの実現

Zigの言語設計における基本的な方針▶ プログラムの振る舞いをすべて記述▶ 隠れた制御フローがない（プログラムは見た目の通りに動く）▶ 手動のメモリ管理（メモリの扱いを明示的に記述）▶ 性能を重視しつつ，安全性も重視する▶ 性能と安全性は同時には両立できないため，ビルドオ
プションにより，性能を取るか安全性を取るかを選択（ReleaseSafeとReleaseFast/ReleaseSmall）▶ ただし，メモリ安全な言語ではない▶ シンプルな文法（小さい言語仕様）▶ ただし，ビルトイン関数と標準ライブラリは別（ビルトイン
関数は100個くらいある．標準ライブラリはそれなりの規模．これは仕方がない）▶ C言語との共存を容易に
Hiroaki Takada

6

Zxx⾔語によるRTOSの実現

Zig言語の重要な特徴（設計方針）▶ コンパイル時のコード実行（comptime）▶ 最適化と合わせて，プリプロセッサが不要に▶ 安全性の向上▶ ポインタの種類分け▶ オプショナル型（NULLポインタの代替）▶ 安全なunion▶ 記述性の向上▶ 新しいエラー処理のアプローチ▶ defer文，ラベル付きのbreak文とcontinue文（goto-less）▶ ジェネリックなデータ構造と関数▶ コンパイル時のリフレクション（メタプログラミング）▶ 非同期関数，並行性のサポート（☆未調査）▶ 単体テスト記述の統合▶ ビルドシステム（Makefileの代替）をZig自身で記述できる
Hiroaki Takada

7

Zxx⾔語によるRTOSの実現

開発者▶ Andrew Kelley氏▶ Zig開発に対する寄附で生活している▶ 寄附は，10ドル/月のプランから（最大6,000ドル/月）▶ 多数のコントリビュータ▶ The Zig Software Foundationを2020年3月に設立▶ ソフトウェア技術者を雇用している
ライセンス▶ 言語処理系は，MITライセンス（一条項BSDライセンスと同等）によるオープンソースソフトウェア
最新版▶ Release 0.6.0（2020年4月にリリース）▶ 毎日スナップショットがリリースされる▶ Release 0.7.0は，2020年9月末？
Hiroaki Takada

8

Zxx⾔語によるRTOSの実現

Zigの思想意図を正確に伝える% zig zen
* Communicate intent precisely.
* Edge cases matter.
* Favor reading code over writing code.
* Only one obvious way to do things.
* Runtime crashes are better than bugs.
* Compile errors are better than runtime crashes.
* Incremental improvements.
* Avoid local maximums.
* Reduce the amount one must remember.
* Minimize energy spent on coding style.
* Together we serve end users.

Hiroaki Takada

9

Zxx⾔語によるRTOSの実現

プログラミング⾔語 Zen☞ https://zen-lang.org/ja-JP/
Zenとは？（Zenのウェブサイトより）▶ Zen言語は，安全で善いコードを漸進的に構築可能なシ
ステムプログラミング言語（Zenの禅：善，全，漸）言語の位置付け▶ Zig（Zig 0.3.0）から派生した言語▶ Zigの主な特徴を継承▶ 独自の拡張機能▶ インタフェース機能など
開発者▶ 帝都久利寿（Kristopher Tate）氏▶ コネクトフリー（本社：京都）▶ Zenに対する有料サポートサービスを提供
Hiroaki Takada

10

Zxx⾔語によるRTOSの実現

ライセンス▶ 言語処理系の利用▶ 内部利用（internal purposes，reference use）は無償▶ 商用利用には，ConnectFree Developer Networkの有料会員になることが必要（$99USD／年・チーム）▶ 公開情報では不明確なところが多い（改善を提案中）▶ 言語処理系のソースコードは，（現時点では）クローズ
最新版▶ Release 0.8.0LTS（2020年4月にリリース）▶ 数週間に1回程度，バージョンアップされる▶ Release 0.9.0の開発が進行中

Hiroaki Takada

11

Zxx⾔語によるRTOSの実現

Zig⾔語およびZen⾔語によるRTOSの実現
実装の概要▶ TOPPERS/ASP3カーネル（ターゲットプロセッサ：ARM）のカーネル本体をZigおよびZen（以下，Zxxと書く）で実装▶ libkernel.aの全体をZxx＋インラインアセンブラで記述することに成功（.cファイル，.Sファイルは追放した）▶ コンフィギュレーション記述（静的API）とコンフィギュレータ（Rubyで記述）もZxxで実装（パス3は除く）▶ Zxxの特性や制限から，一部の仕様を変更▶ アプリケーションとシステムサービス（C言語＋TECSで記述）は修正なし▶ サンプルアプリケーションのZxx版も作成▶ ビルドシステム（Makefile）には修正を加えた▶ ここもZxxで実現したいが，現時点ではできていない
Hiroaki Takada

12

Zxx⾔語によるRTOSの実現

ZxxによるRTOSの実装コード例
以下では…
▶ Zigで記述したASP3カーネル本体の実際のコードの一部を示す▶ C言語（点線の囲みの中）とZig（実線の囲みの中）を比較して解説▶ ZigとZenのコードの違い（わずか）も示す▶ 分かりやすさのために，少し加工してある示すコード▶ サービスコール（act_tsk）▶ エラーセット型の定義▶ act_tskのC言語API

Hiroaki Takada

13

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;
traceLog("actTskEnter", .{ tskid });errdefer |err| traceLog("actTskLeave", .{ err });try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()) {p_tcb = p_runtsk.?;}else {
p_tcb = try checkAndGetTCB(tskid);}{target_impl.lockCpu();defer target_impl.unlockCpu();

LOG_ACT_TSK_ENTER(tskid);
CHECK_UNL();if (tskid == TSK_SELF && !sense_context()) {p_tcb = p_runtsk;} else {CHECK_ID(VALID_TSKID(tskid));p_tcb = get_tcb(tskid);}
lock_cpu();
if (TSTAT_DORMANT(p_tcb->tstat)) {make_active(p_tcb);
if (p_runtsk != p_schedtsk) {if (!sense_context()) {dispatch();} else {request_dispatch_retint();}}
ercd = E_OK;}
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)!= 0U || p_tcb->actque == TMAX_ACTCNT) {ercd = E_QOVR;} else {p_tcb->actque += 1;
ercd = E_OK;}
unlock_cpu();
error_exit:
LOG_ACT_TSK_LEAVE(ercd);
return(ercd);

if (isDormant(p_tcb.tstat)) {make_active(p_tcb);
if (p_runtsk != p_schedtsk) {if (!target_impl.senseContext()) {target_impl.dispatch();} else {target_impl.requestDispatchRetint();}}}
else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
or p_tcb.flags.actque == TMAX_ACTCNT) {return ItronError.QueueingOverflow;}
else {
p_tcb.flags.actque += 1;}}
traceLog("actTskLeave", .{ null });}}Hiroaki Takada

14

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

API名はそのまま(Zigの命名ガイドラインを逸脱)

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter", .{ tskid });LOG_ACT_TSK_ENTER(tskid);|err| traceLog("actTskLeave", .{ err });ファイル外に⾒せる errdefer
CHECK_UNL();
try 関数checkContextUnlock();関数の戻り値の型 {if (tskid == TSK_SELF and !target_impl.senseContext())
if (tskid == TSK_SELF && !sense_context()){
C⾔語のstaticの反意
p_tcb = p_runtsk.?;p_tcb = p_runtsk;
ItronError型 (エラー
「引数名: 型名」}
} else {セット型) を返すか
else {CHECK_ID(VALID_TSKID(tskid));
引数はconst
p_tcb = try checkAndGetTCB(tskid); 何も返さない
p_tcb = get_tcb(tskid);}
constは，定数というより， }関数内で代⼊lock_cpu();{
定義した後に値を変えられない
できない
if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();
変数と理解した⽅が良い
make_active(p_tcb);
defer target_impl.unlockCpu();
if (p_runtsk != p_schedtsk) {if (!sense_context()) {if (isDormant(p_tcb.tstat)) {make_active(p_tcb);
dispatch();値で渡すかポインタで渡すかは，
if (p_runtsk != p_schedtsk) {} else {コンパイラが決める
if (!target_impl.senseContext()){
request_dispatch_retint();
target_impl.dispatch();}} else {}target_impl.requestDispatchRetint();
ercd = E_OK;}}
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)}!= 0U || p_tcb->actque == TMAX_ACTCNT) {}ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();}}
error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

15

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter", .{ tskid });LOG_ACT_TSK_ENTER(tskid);(ローカル) 変数 errdefer |err| traceLog("actTskLeave", .{ err });CHECK_UNL();
try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()){
if (tskid == TSK_SELF && !sense_context()) {初期値は不定値
TCB型へのポインタ
p_tcb = p_runtsk.?;p_tcb = p_runtsk;(動作が未定義になるので}} else {else {CHECK_ID(VALID_TSKID(tskid));
推奨されない)p_tcb = try checkAndGetTCB(tskid);p_tcb = get_tcb(tskid);}}
lock_cpu();{
Zenでは，
初期値は必ず記述
if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();“*mut TCB” とmake_active(p_tcb);
defer target_impl.unlockCpu(); しなければならない
することが必要
if (p_runtsk != p_schedtsk) {if (!sense_context()) {if (isDormant(p_tcb.tstat)) {make_active(p_tcb);
dispatch();if (p_runtsk != p_schedtsk) {} else {if (!target_impl.senseContext()) {request_dispatch_retint();
target_impl.dispatch();}} else {}target_impl.requestDispatchRetint();
ercd = E_OK;}}
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)}!= 0U || p_tcb->actque == TMAX_ACTCNT) {}ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();}}
error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

16

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

Zigの命名ガイドラインに従った

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter", .{ tskid });LOG_ACT_TSK_ENTER(tskid);
errdefer |err| traceLog("actTskLeave", .{ err });CHECK_UNL();
try checkContextUnlock();
この場合は関数if (tskid == TSK_SELF and !target_impl.senseContext()) {if (tskid == TSK_SELF && !sense_context()){
p_tcb = p_runtsk.?;p_tcb = p_runtsk;
この変数にエラーを}} else {CHECK_ID(VALID_TSKID(tskid)); このブロックからelse {受け取る
p_tcb = get_tcb(tskid);
エラーで抜ける時に p_tcb = try checkAndGetTCB(tskid);}}
後ろの⽂を実⾏する
lock_cpu();{
if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();
make_active(p_tcb);
defer target_impl.unlockCpu();
if (p_runtsk != p_schedtsk) {任意の型のパラメータのリスト
if (!sense_context()) {if (isDormant(p_tcb.tstat)){make_active(p_tcb); C⾔語のvarargに代わるもの
dispatch();if (p_runtsk != p_schedtsk) {} else {if (!target_impl.senseContext()) {request_dispatch_retint();
option.logネームスペースに，
target_impl.dispatch();}
⽂字列の型
actTskLeaveの定義があるか︖} else {}target_impl.requestDispatchRetint();
ercd = E_OK;}}
else if ((p_tcb->p_tinib->tskatr & pub
TA_NOACTQUE)} log_type: []const u8, args: var) void {fn traceLog(comptime!= 0U || p_tcb->actque == TMAX_ACTCNT) {}if (@hasDecl(option.log,
log_type)) {ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr& TA_NOACTQUE) != 0
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {@field(option.log, log_type)(args);
p_tcb->actque += 1;
return ItronError.QueueingOverflow;}
ercd = E_OK;}}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();}}
error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

エラーでない場合は，nullを渡す

17

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter", .{ tskid });LOG_ACT_TSK_ENTER(tskid);
errdefer |err| traceLog("actTskLeave", .{ err });CHECK_UNL();
try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()) {if (tskid == TSK_SELF && !sense_context()) {p_tcb = p_runtsk.?;p_tcb = p_runtsk;}} else {else {CHECK_ID(VALID_TSKID(tskid));
エラーの場合，右の式がエラーを返した場合，
p_tcb = try checkAndGetTCB(tskid);p_tcb=
get_tcb(tskid);goto
error_exitする
そのエラーをreturnする}}
lock_cpu();{(⼤域脱出的)if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();
隠れた制御フローがある︕
make_active(p_tcb);
defer target_impl.unlockCpu();
if (p_runtsk != p_schedtsk) {if (!sense_context()) {if (isDormant(p_tcb.tstat)) {下のように書くこともできる
make_active(p_tcb);
dispatch();if (p_runtsk != p_schedtsk) {} else {checkContextUnlock()catch |err| return err; {if (!target_impl.senseContext())
request_dispatch_retint();
target_impl.dispatch();}} else {}target_impl.requestDispatchRetint();
ercd = E_OK;
左の式がエラーを返した場合，
この変数にエラーを}}
右の式を実⾏する
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)}
受け取る!= 0U || p_tcb->actque == TMAX_ACTCNT) {}ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();}}
error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

18

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

ItronError型 (エラー

traceLog("actTskEnter", .{ tskid });
セット型)
を返すか
LOG_ACT_TSK_ENTER(tskid);
errdefer |err| traceLog("actTskLeave",.{ err });CHECK_UNL();
try checkContextUnlock();
何も返さないif (tskid == TSK_SELF and !target_impl.senseContext()) {if (tskid == TSK_SELF && !sense_context()) {p_tcb = p_runtsk.?;p_tcb = p_runtsk;}} else {else {CHECK_ID(VALID_TSKID(tskid));p_tcb = try checkAndGetTCB(tskid);= get_tcb(tskid);#definep_tcb
CHECK_UNL()
do { ¥
pub fn checkContextUnlock()
ItronError!void {}}if (sense_lock()) { ¥lock_cpu();{
if (target_impl.senseLock()) {ercd = E_CTX;¥
if (TSTAT_DORMANT(p_tcb->tstat)){target_impl.lockCpu();
returnItronError.ContextError;
make_active(p_tcb);
defertarget_impl.unlockCpu();
goto error_exit; ¥}if (p_runtsk != p_schedtsk) {}¥if (!sense_context()){
if (isDormant(p_tcb.tstat)) {}} while (false)
make_active(p_tcb);
dispatch();if (p_runtsk != p_schedtsk) ItronError型の{} else {ContextErrorを返す
if(!target_impl.senseContext()){
request_dispatch_retint();
elseの場合はtarget_impl.dispatch();}
何も返さない } else {}target_impl.requestDispatchRetint();
ercd = E_OK;}}
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)}!= 0U || p_tcb->actque == TMAX_ACTCNT) {}ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();}}
error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

19

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

ターゲット依存部はネームスペースを分けた
pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter",.{ tskid });ネームスペース
LOG_ACT_TSK_ENTER(tskid);
errdefer |err| traceLog("actTskLeave", .{ err });CHECK_UNL();
try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()) {if (tskid == TSK_SELF && !sense_context()) {p_tcb = p_runtsk.?;p_tcb = p_runtsk;}} else {else {CHECK_ID(VALID_TSKID(tskid));p_tcb = try checkAndGetTCB(tskid);p_tcb = get_tcb(tskid);}}
nullでない場合に，
p_runtskは?*TCB型
lock_cpu();{
データを取り出す
if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();?はオプショナル型を⽰す
make_active(p_tcb);
defer target_impl.unlockCpu();(危険な記述なのでTCBへのポインタ
if (p_runtsk != p_schedtsk) {推奨されない)if (!sense_context()) {if (isDormant(p_tcb.tstat)){またはnullが代⼊できる
make_active(p_tcb);
dispatch();if (p_runtsk != p_schedtsk) {} else {if (!target_impl.senseContext()) {request_dispatch_retint();
nullであった場合，}ポインタの値は0になれないため， target_impl.dispatch();} else {}内部的にはnullは0で表される
ReleaseSafeなら検出されtarget_impl.requestDispatchRetint();
ercd = E_OK;}}
panicになる
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)}
ReleaseFast/Smallでは!= 0U || p_tcb->actque== TMAX_ACTCNT) {}ポインタの値に0を
ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr& TA_NOACTQUE) != 0
検出されない許すという宣⾔もある} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();}}
error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

20

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;
ER
ercd;

}

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter", .{ tskid });LOG_ACT_TSK_ENTER(tskid);
errdefer |err| traceLog("actTskLeave", .{ err });右の式がエラーならCHECK_UNL();
try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()) {if (tskid == TSK_SELF && !sense_context()) { 関数からエラーリターン
p_tcb = p_runtsk.?;p_tcb = p_runtsk;}} else {else {CHECK_ID(VALID_TSKID(tskid));p_tcb = try checkAndGetTCB(tskid);p_tcb = get_tcb(tskid);}}
lock_cpu();{
if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();
make_active(p_tcb);
defer target_impl.unlockCpu();
if (p_runtsk != p_schedtsk) {if (!sense_context()) {if (isDormant(p_tcb.tstat)) {dispatch();pub fn checkAndGetTCB(tskid:make_active(p_tcb);
ID) ItronError!*TCB {if (p_runtsk != p_schedtsk) {} else {try checkId(TMIN_TSKID <= iftskid
and tskid <= cfg._kernel_tmax_tskid);(!target_impl.senseContext()){
request_dispatch_retint();
target_impl.dispatch();}return &cfg._kernel_tcb_table[indexTask(tskid)];} else {}}target_impl.requestDispatchRetint();
ercd = E_OK;}}
else if ((p_tcb->p_tinib->tskatr& TA_NOACTQUE)}
tskidが範囲内なら，そのタスクのtskidが有効範囲外なら，!= 0U || p_tcb->actque == TMAX_ACTCNT) {}TCBの先頭番地を返す
エラーを返すercd = E_QOVR;
else if((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}
pub fn checkId(ok: elsebool)}
{ ItronError!void {p_tcb.flags.actque+= 1;
unlock_cpu();
if (!ok) {}return ItronError.IdError;}
error_exit:}
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

elseの場合は何も返さないItronError型のIdErrorを返す

21

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;

traceLog("actTskEnter", .{ tskid });LOG_ACT_TSK_ENTER(tskid);
errdefer |err| traceLog("actTskLeave", .{ err });CHECK_UNL();
try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()) {if (tskid == TSK_SELF && !sense_context()) {p_tcb = p_runtsk.?;p_tcb = p_runtsk;}} else {else {CHECK_ID(VALID_TSKID(tskid));p_tcb = try checkAndGetTCB(tskid);p_tcb = get_tcb(tskid);}}
lock_cpu();{
if (TSTAT_DORMANT(p_tcb->tstat)) {target_impl.lockCpu();
make_active(p_tcb);
defer target_impl.unlockCpu();
if (p_runtsk != p_schedtsk) {if (!sense_context()) {if (isDormant(p_tcb.tstat)) {make_active(p_tcb);
dispatch();if (p_runtsk != p_schedtsk) {} else {このブロックから
if (!target_impl.senseContext()) {request_dispatch_retint();
target_impl.dispatch();}
抜ける時に} else {}後ろの⽂を実⾏する
target_impl.requestDispatchRetint();
ercd = E_OK;}}
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)}!= 0U || p_tcb->actque == TMAX_ACTCNT){}
ブロック中から
ercd = E_QOVR;
else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
returnしてもOK!
} else {or p_tcb.flags.actque == TMAX_ACTCNT) {p_tcb->actque += 1;
return ItronError.QueueingOverflow;
ercd = E_OK;}}
else {
p_tcb.flags.actque += 1;
unlock_cpu();
ブロック中からreturn}}error_exit:
traceLog("actTskLeave", .{ null });LOG_ACT_TSK_LEAVE(ercd);}
return(ercd);}

Hiroaki Takada

22

Zxx⾔語によるRTOSの実現

サービスコール（act_tsk）
ER act_tsk(ID tskid) {TCB*p_tcb;ER
ercd;

pub fn act_tsk(tskid: ID) ItronError!void {var p_tcb: *TCB = undefined;
traceLog("actTskEnter", .{ tskid });errdefer |err| traceLog("actTskLeave", .{ err });try checkContextUnlock();
if (tskid == TSK_SELF and !target_impl.senseContext()) {p_tcb = p_runtsk.?;}else {
p_tcb = try checkAndGetTCB(tskid);}{target_impl.lockCpu();defer target_impl.unlockCpu();

LOG_ACT_TSK_ENTER(tskid);
CHECK_UNL();if (tskid == TSK_SELF && !sense_context()) {p_tcb = p_runtsk;} else {CHECK_ID(VALID_TSKID(tskid));p_tcb = get_tcb(tskid);}
lock_cpu();
if (TSTAT_DORMANT(p_tcb->tstat)) {make_active(p_tcb);
if (p_runtsk != p_schedtsk) {if (!sense_context()) {dispatch();} else {request_dispatch_retint();}}
ercd = E_OK;}
else if ((p_tcb->p_tinib->tskatr & TA_NOACTQUE)!= 0U || p_tcb->actque == TMAX_ACTCNT) {ercd = E_QOVR;} else {p_tcb->actque += 1;
ercd = E_OK;}
unlock_cpu();
error_exit:
LOG_ACT_TSK_LEAVE(ercd);
return(ercd);

if (isDormant(p_tcb.tstat)) {make_active(p_tcb);
if (p_runtsk != p_schedtsk) {if (!target_impl.senseContext()) {ポインタ型が左辺の“.” はtarget_impl.dispatch();} else {C⾔語の “->”target_impl.requestDispatchRetint();の意味になる}}}else if ((p_tcb.p_tinib.tskatr & TA_NOACTQUE) != 0
or p_tcb.flags.actque == TMAX_ACTCNT) {return ItronError.QueueingOverflow;}
else {
p_tcb.flags.actque += 1;}}
traceLog("actTskLeave", .{ null });}}Hiroaki Takada

23

Zxx⾔語によるRTOSの実現

エラーセット型の定義

act_tskのC言語API////// サービスコールにおけるエラー///
pub const ItronError = error {SystemError,
NotSupported,
ReservedFunction,
ReservedAttribute,
ParameterError,
IdError,ContextError,
MemoryAccessViolation,
ObjectAccessViolation,
IllegalUse,
NoMemory,
NoId,
NoResource,
ObjectStateError,
NonExistent,
QueueingOverflow,
ReleasedFromWaiting,TimeoutError,
ObjectDeleted,
ConnectionClosed,
TerminationRequestRaised,
WouldBlock,
BufferOverflow,
CommunicationError,};// C言語ヘッダファイルの取り込み
pub const c = @cImport({@cDefine("UINT_C(val)", "val");@cInclude("kernel.h");});Hiroaki Takada// C言語APIのエラーコードへの変換
fn itronErrorCode(err: ItronError) ER {return switch (err) {ItronError.SystemError => c.E_SYS,
ItronError.NotSupported => c.E_NOSPT,
ItronError.ReservedFunction => c.E_RSFN,
…………
};
}
// 返値をER型に変換
fn callService(result: ItronError!void) ER {if (result) {return c.E_OK;}
else |err| {return itronErrorCode(err);}}// act_tskのC言語API
export fn act_tsk(tskid: ID) ER {return callService(task_manage.act_tsk(tskid));}24

Zxx⾔語によるRTOSの実現
ネームスペース

エラーセット型の定義

C⾔語ヘッダファイルのact_tskのC言語API
インクルード////// サービスコールにおけるエラー///
pub const ItronError = error {SystemError,
NotSupported,enum (列挙型) に
ReservedFunction,
似ている
ReservedAttribute,
ParameterError,
IdError,ContextError,
MemoryAccessViolation,
ObjectAccessViolation,
IllegalUse,
NoMemory,
NoId,
NoResource,
ObjectStateError,
resultがエラーでNonExistent,
ない場合にifが成⽴
QueueingOverflow,
ReleasedFromWaiting,TimeoutError,
ObjectDeleted,
ConnectionClosed,
TerminationRequestRaised,
WouldBlock,
BufferOverflow,
Ｃ⾔語から
CommunicationError,
呼べるようにする};// C言語ヘッダファイルの取り込み
pub const c = @cImport({@cDefine("UINT_C(val)", "val");@cInclude("kernel.h");
C⾔語ヘッダファイルから});

ItronError型 (エラーセット型) の定義

Hiroaki Takada

libkernel.aの外部から呼べるようにする

取り込んだ定数値// C言語APIのエラーコードへの変換
fn itronErrorCode(err: ItronError) ER {return switch (err) {ItronError.SystemError => c.E_SYS,
ItronError.NotSupported => c.E_NOSPT,
ItronError.ReservedFunction => c.E_RSFN,…………
};
ItronError型 (エラー}セット型) またはvoid// 返値をER型に変換
fn callService(result: ItronError!void) ER {if (result) {return c.E_OK;
この変数にエラーを}受け取る
else |err| {return itronErrorCode(err);}}// act_tskのC言語API
export fn act_tsk(tskid: ID) ER {return callService(task_manage.act_tsk(tskid));}

ネームスペース25

Zxx⾔語によるRTOSの実現

静的APIとコンフィギュレータの置き換え
静的APIとコンフィギュレータ

静的API

appl.cfg

システムコンフィギュ
レーションファイル#include “appl.h”
CRE_TSK(TASK1, { TA_ACT, …
CRE_SEM(SEM1, { TA_TPRI, …

コンフィギュレータ

コンフィギュレータ
コンフィギュレータ
コンフィギュレータ
設定ファイル
設定ファイル
設定ファイル（内部的には複雑なステップ）
kernel_cfg.ckernel_cfg.h

カーネル構成・初期化
ファイル

カーネル構成・初期化
ヘッダファイル

Hiroaki Takada#define TASK1 3#define SEM1 526

Zxx⾔語によるRTOSの実現

静的APIとコンフィギュレータの入出力の例
システムコンフィギュレーションファイル
CRE_TSK( TASK1, { TA_ACT, 0, task_main,
10, STACK_SIZE, NULL } );kernel_cfg.h#define TASK1

3

コンフィギュ
レータ

kernel_cfg.cstatic STK_T _kernel_stack_TASK1[COUNT_STK_T(STACK_SIZE)];
const TINIB _kernel_tinib_table[TNUM_TSKID] = {スタック領域……
{ (TA_ACT), (intptr_t)(0), ((TASK)(task_main)),
INT_PRIORITY(10), ROUND_STK_T(STACK_SIZE),_kernel_stack_TASK1 },……
タスク初期化ブロック
タスク管理ブロック};
TCB _kernel_tcb_table[TNUM_TSKID];
Hiroaki Takada

27

Zxx⾔語によるRTOSの実現

Zxxによる静的APIとコンフィギュレータの置き換えの目標▶ 静的API（オブジェクトの生成記述）を，Zxxで記述する▶ 文法が少し変わってしまうのは，許容する▶ コンフィギュレータ（に相当する処理）をZxxで記述する▶ カーネル構成・初期化ファイル（kernel_cfg.c）をZxxで生成するのではなく，直接データ構造を生成する▶ コンフィギュレータのパス1は不要になる▶ パス3は，まずはスコープ外とする（1つのパスに統合するのは難しいため）▶ オブジェクトを動的に生成するサービスコールのコードが，そのまま静的に（コンパイル時に）実行されれば理想的だ
が，それは難しいと思われる▶ どこまで近いコードにできるかはトライしていない
Hiroaki Takada

28

Zxx⾔語によるRTOSの実現

静的API vs. Zigによるコンフィギュレーション記述
静的APIによるコンフィギュレー
ション記述

INCLUDE("tecsgen.cfg");#include "test_sem2.h"
CRE_TSK(TASK1, { TA_ACT, 1, task1, MID_PRIORITY, STACK_SIZE, NULL });CRE_TSK(TASK2, { TA_NULL, 2, task2, HIGH_PRIORITY, STACK_SIZE, NULL });CRE_TSK(TASK3, { TA_NULL, 3, task3, LOW_PRIORITY, STACK_SIZE, NULL });CRE_ALM(ALM1, { TA_NULL, { TNFY_HANDLER, 1, alarm1_handler }});
CRE_SEM(SEM1, { TA_NULL, 1, 1 });CRE_SEM(SEM2, { TA_TPRI, 0, 1 });Zigによるコンフィギュレー
ション記述

usingnamespace @import("../kernel/kernel_cfg.zig");
const tecs = @import("../" ++ TECSGENDIR ++ "/tecsgen_cfg.zig");usingnamespace @cImport({@cDefine("UINT_C(val)", "val");@cInclude("test_sem2.h");});Zenでは，“*mut CfgData” とすることが必要

fn configuration(comptime cfg: *CfgData) void {tecs.configuration(cfg);
cfg.CRE_TSK("TASK1", CTSK(TA_ACT, 1, task1, MID_PRIORITY, STACK_SIZE, null));cfg.CRE_TSK("TASK2", CTSK(TA_NULL, 2, task2, HIGH_PRIORITY, STACK_SIZE, null));cfg.CRE_TSK("TASK3", CTSK(TA_NULL, 3, task3, LOW_PRIORITY, STACK_SIZE, null));cfg.CRE_ALM("ALM1", CALM(TA_NULL, NFY_TMEHDR(1, alarm1_handler)));cfg.CRE_SEM("SEM1", CSEM(TA_NULL, 1, 1));
cfg.CRE_SEM("SEM2", CSEM(TA_TPRI, 0, 1));}// 静的APIの読み込みとコンフィギュレーションデータの生成// 以下は変更する必要がない．
＜以下省略 ＞

Hiroaki Takada

29

Zxx⾔語によるRTOSの実現

静的API vs. Zigによるコンフィギュレーション記述 − 拡大図
INCLUDE("tecsgen.cfg");
CRE_TSK(TASK1, { TA_ACT, 1, task1, MID_PRIORITY,
STACK_SIZE, NULL });システムコンフィギュレー
ション記述を⾏う関数

コンフィギュレーション
データを格納するデータ構造

fn configuration(comptime cfg: *CfgData) void {tecs.configuration(cfg);
cfg.CRE_TSK("TASK1", CTSK(TA_ACT, 1, task1,
MID_PRIORITY, STACK_SIZE, null));}
データ構造にタスクの⽣成情報を追加する関数

これらの記述をすべてコンパイル時に実⾏し，コンフィギュレーションデータを静的に⽣成
Hiroaki Takada

30

Zxx⾔語によるRTOSの実現

コンフィギュレータ実装のアプローチ▶ アプローチ1▶ カーネル本体（libkernel.a）とコンフィギュレーション記述
は個別にコンパイルし，リンカにより結合▶ カーネル本体のバイナリコードは，コンフィギュレーショ
ンによって変化しないüコンフィギュレーション変更時の再コンパイルは不要▶ アプローチ2▶ カーネル本体（libkernel.a）とコンフィギュレーション記述をまとめてコンパイル▶ コンフィギュレーションに最適化されたカーネルのバイ
ナリコードが生成される
ü例えば，使用するセマフォが1つだけなら，セマフォの初期化処理のループは展開されるだろう▶ テスト（品質保証）の観点からは良いかどうか疑問
Hiroaki Takada

31

Zxx⾔語によるRTOSの実現

コンフィギュレータ実装のアプローチ（続き）▶ コンパイルオプションで，どちらのアプローチを使うかを変えられるように実装▶ さらに，アプリケーション（システムサービスも含む）もZxxで記述し，カーネルやコンフィギュレーション記述とまとめてコンパイルするアプローチも考えられる（未実装）
アプリケーション（システムサービスを含む）
呼出し
カーネル本体(libkernel.a)

Hiroaki Takada

参照
読出し

コンフィギュレー
ション記述

32

Zxx⾔語によるRTOSの実現

実現したRTOSの性能評価

修正スライドプログラムサイズの比較（1）▶ ASP3カーネル（ターゲット：CT11MPCore）の標準サンプ
ルプログラム（sample1）▶ カーネル本体とコンフィギュレーション記述を個別にコンパイルした場合† カーネルのみClangでコンパイル
Zig

Zig

Zig

ビルド条件

GCC
(NDEBUG)(ReleaseSafe)(ReleaseFast)(ReleaseSmall)

プログラム
サイズ

30,680B

41,497B

35,397B

34,793B

ビルド条件

Clang †

Zen

Zen

Zen

プログラム
サイズ

(NDEBUG)(ReleaseSafe)(ReleaseFast)(ReleaseSmall)

31,748B

41,495B

35,425B

34,821B▶ ReleaseSafeは，35%程度大きい（やむをえない）▶ ReleaseFast/Smallは，15%程度大きい
Hiroaki Takada

33

Zxx⾔語によるRTOSの実現

修正スライドプログラムサイズの比較（2）▶ ASP3カーネル（ターゲット：CT11MPCore）の標準サンプ
ルプログラム（sample1）▶ カーネル本体とコンフィギュレーション記述をまとめてコンパイルした場合† カーネルのみClangでコンパイル
ビルド条件

GCC

Zig

Zig

Zig

(NDEBUG)(ReleaseSafe)(ReleaseFast)(ReleaseSmall)

プログラム
サイズ

30,680B

39,185B

34,549B

34,005B

ビルド条件

Clang †

Zen

Zen

Zen

プログラム
サイズ

(NDEBUG)(ReleaseSafe)(ReleaseFast)(ReleaseSmall)

31,748B

39,191B

34,569B

34,025B▶ 個別にコンパイルした場合と比べて，3〜5%程度小さい▶ コードサイズが大きくなる主な原因は，関数を積極的にインライン展開することと思われる
Hiroaki Takada

34

Zxx⾔語によるRTOSの実現

実行時間の比較▶ ASP3カーネル（ターゲット：GR-PEACH）▶ タスクを起動するサービスコール（act_tsk）によるタスク切
換え時間
ビルド条件

実行時間

C

Zig

Zig

Zig

(NDEBUG)(ReleaseSafe)(ReleaseFast)(ReleaseSmall)

1.6μsec

1.4μsec

1.3μsec

1.4μsec

ビルド条件

Zen

Zen

Zen

(ReleaseSafe)(ReleaseFast)(ReleaseSmall)

1.4μsec

1.3μsec

1.4μsec

実行時間▶ C言語実装と比べて，15〜20%程度高速▶ 実行時間が短くなる主な原因は，関数を積極的にインライン展開することと思われる▶ ReleaseFastとReleaseSmallの差は小さい▶ もっと差をつけても良いように思う
Hiroaki Takada

35

Zxx⾔語によるRTOSの実現

Zig⾔語およびZen⾔語の現時点での評価
記述性の評価▶ 記述が，小さくはならないが，きれいにはなっており，安全性や保守性は向上していると思われる▶ エラー処理がきれいに記述できる（gotoが不要に）▶ ジェネリック型とジェネリック関数は強力かつシンプル▶ バリエーションの記述方法には工夫とルール化が必要▶ 低レベルのビルトイン関数が便利▶ C言語（.hファイル，.cファイル），Ruby言語，静的APIの記述を，ほぼすべてZxxに統一できた性能（実行時間，メモリ使用量）の評価▶ 実行時間は短くなっているが，コードサイズが大きくなっているのは気になる▶ ReleaseSmallは，もっとコードサイズを小さくする側に振るべきと思われる
Hiroaki Takada

36

Zxx⾔語によるRTOSの実現

Zxxの課題▶ まだまだ未完成の言語▶ 未実装の機能，制限事項も多数残っている▶ 言語処理系の不具合も多い▶ 安全性と性能に関する概念と方針の整理 … 後で議論
▶ 分割コンパイルのサポート▶ ドキュメントの不足・不備（未完成部分）▶ 不具合なのか仕様なのか判断できない▶ Zenのドキュメントは読みやすい▶ エラーメッセージが不親切▶ エラーメッセージの出方（特に出る順序）が違う▶ 正しく動かすまでに試行錯誤が必要▶ ターゲットプロセッサが限られる▶ llvmが対応していることが必要条件
Hiroaki Takada

37

Zxx⾔語によるRTOSの実現

ZigとZenで書き換えが必要だった主な箇所▶ ファイルの拡張子の変更（“.zig” → “.zen” ）▶ mut 指定
▶ Zenでは，ポインタ はconst（指している先に書き込めない）が標準．Zigにおけるconstは削除して，そうでないポインタに mut を付けることが必要（そう単純ではない）▶ 構造体の外部から参照されるフィールドにpub指定を追加▶ ビルトイン関数名の違いへの対応▶ @as（Zig） と @to（Zen）▶ 標準ライブラリの仕様の違い（多数あり）への対応▶ Zenではグローバル変数を，undefined以外に初期化することが必要▶ コンパイラの不具合の回避策▶ 多くの不具合は，両言語のコンパイラに共通しており，回避策も共通だったりする
Hiroaki Takada

38

Zxx⾔語によるRTOSの実現

議論 − プログラムの安全性と性能▶ RTOSの特性と設計▶ 性能が重視される▶ 機能間の結びつきが密接▶ その結果，安全でない設計になっている▶ とは言え，安全でないコードを特定・局所化し，その部分を集中的に検証したい▶ C言語ではMISRA-Cからの逸脱▶ Rust（等）ではunsafeブロック▶ Zxxでは，現時点では，安全でない構文要素が明示されていない（考えればだいたいわかるが）! まずは，安全性に対する概念と方針の整理が必要と思われる
Hiroaki Takada

39

Zxx⾔語によるRTOSの実現

プログラミング⾔語の安全性「プログラミング言語の安全性」とは？
▶ 様々な定義がある▶ ここでは，「安全なプログラミング言語で記述したプログラ
ムは，ある種の不正動作をしない」と定義する▶ 定義の後半は，「ある種の不正動作から保護されている」と言い換えても良い
メモリ安全性▶ バッファオーバフローやダングリングポインタなどの，RAM
アクセス時に発生するバグやセキュリティホールなどから保護されている状態（Wikipediaより）▶ 上の定義の「不正動作」が「RAMアクセス時に発生するバグやセキュリティホールなど」ということ▶ より厳密には，「不正動作」が「定義されていないメモリアクセス」（この厳密な定義が難しい）という感じになる
Hiroaki Takada

40

Zxx⾔語によるRTOSの実現

配列アクセスのメモリ安全性（範囲検査）を例に考える▶ C，C++の場合▶ []による配列アクセスでは，範囲を超えたアクセスは検出されず，安全でない▶ Java（安全な言語とされている）の場合▶ []による配列アクセスでは，範囲を超えたアクセスを行うと，実行時に検出されて例外が発生する（回復可能）▶ Rust （安全な言語とされている）の場合▶ []による配列アクセスでは，範囲を超えたアクセスを行うと，実行時に検出されてpanicになる（回復不可能）▶ getによる配列アクセスはオプショナル型を返す
ü 範囲を超えたアクセスを行うと，実行時に検出されてNoneが返る
ü オプショナル型を返すので，例外処理を書くことを強制される（例外はcatchしないことができる）
Hiroaki Takada

41

Zxx⾔語によるRTOSの実現

配列アクセスのメモリ安全性（範囲検査）を例に考える − 続き
▶ “zig zen”より▶ Runtime crashes are better than bugs.
▶ Compile errors are better than runtime crashes.
▶ 理想は，コンパイル時に範囲を超えたアクセスを行う可能性があることが検出され，コンパイルエラーになることだが，これは難しい▶ Zxxの場合▶ []による配列アクセスでは，範囲を超えたアクセスを行うと，実行時に検出されpanicになる▶ Rustと同様のgetによる配列アクセス関数を用意すること
で，安全性を向上できると思われる

Hiroaki Takada

42

Zxx⾔語によるRTOSの実現

配列アクセスのメモリ安全性（範囲検査）を例に考える − 続き// 5つの符号なし整数からなる配列
const a = [5]u32{ 1, 2, 3, 4, 5 };オプショナル型
u32またはnullを返す

fn get(array: []u32, index: usize) ?u32 {return if (index < array.len) array[index] else null;}get(&a,1)をそのままxに代⼊することはできない
test "" {var x: u32 = undefined;
返り値がnullでない場合にifが成⽴し，x1にu32型の値が⼊る
if (get(&a, 1)) |x1| {x = x1;x1はu32型なので，代⼊できる}
else {// ここに例外処理を書く}
std.debug.warn(”a[1] = {}¥n", .{ x });}Hiroaki Takada

43

Zxx⾔語によるRTOSの実現「不正動作から保護されている」の4つのレベル(1) 静的安全(1-1) 不正動作をする可能性のあるプログラムは，コンパイラがエラーとする
静的安全に分類してみた(1-2) プログラムの不正動作は実行時に（不正動作する
前に）検出され，不正動作をしないようにプログラムを書くことが強制される
Zxxにはない(2) 実行時安全(2-1) プログラムの不正動作は，実行時に（不正動作する
前に）検出されて，回復可能な例外を発生させる(2-2) プログラムの不正動作は，実行時に（不正動作する
前に）検出されて，回復不可能なpanicになる
Hiroaki Takada

44

Zxx⾔語によるRTOSの実現

配列アクセスのメモリ安全性（範囲検査）の例では…
▶ C，C++の場合▶ []による配列アクセスは，安全でない▶ Java（安全な言語とされている）の場合▶ []による配列アクセスは，実行時安全 (2-1)▶ Rust （安全な言語とされている）の場合▶ []による配列アクセスは，実行時安全 (2-2)▶ getによる配列アクセスは，静的安全 (1-2)▶ Zxxの場合▶ []による配列アクセスは， ReleaseSafeでは，実行時安

全 (2-2)▶ Rustと同等のgetによる配列アクセスを用意することで，静的安全 (1-2) にできる
Hiroaki Takada

45

Zxx⾔語によるRTOSの実現

今後の取り組みとソースコードへのアクセス
残っている開発項目▶ すべてを（アプリケーションも）まとめてコンパイルする▶ Zxxのビルドシステムの利用▶ Zenの拡張機能（インタフェース）の活用▶ ソースコードの整理，ドキュメントの作成
ソースコードへのアクセス▶ TOPPERS/ASP3のZigによる実装は，Github上で公開中☞ https://github.com/toppers/asp3_in_zig▶ Zenによる実装も，公開を検討中
その後の取り組み（検討中）▶ Zig言語やZen言語の将来性を見極めて，さらに発展させることも考えたい▶ ご意見やニーズをお聞かせください
Hiroaki Takada

46

Zxx⾔語によるRTOSの実現

TOPPERSについてより詳しく知りたい⽅のために
NEP／enPiT-Proの講座 「RTOSの内部構造」☞ https://www.nces.i.nagoya-u.ac.jp/NEP/courses/2020/T01.html▶ 9月12日（土）・19日（土），オンライン開催▶ TOPPERS/ASP3カーネルのソースコード（C言語版です）

を解説▶ Zxx版のTOPPERS/ASP3カーネルのソースコードを読んでみたい人には役立つはず
TOPPERS開発者会議▶ 2020年9月27日（日）～28日（月），オンライン開催▶ TOPPERS会員以外も参加可能

Hiroaki Takada

47

Zxx⾔語によるRTOSの実現

謝辞▶ TOPPERS/ASP3カーネルをZigおよびZenで実装するにあたり，Zigについていろいろ教えてくれた河田智明君＠高田研究室に感謝します▶ Zig処理系の不具合の切り分け▶ 不具合の回避策の提示▶ Zigの開発者のAndrew Kelley氏らにも感謝します▶ Zenを使用するにあたり，サポートいただいた帝都久利寿
氏とコネクトフリーの皆様に感謝します▶ Zen処理系の不具合の修正▶ 言語仕様（制限事項）の説明，回避策の提示

Hiroaki Takada

48

Zxx⾔語によるRTOSの実現

付録

Hiroaki Takada

49

Zxx⾔語によるRTOSの実現

Zigの⾔語仕様のチラ⾒せ︓ポインタとその仲間
C言語のポインタの問題点▶ 1つの要素を指しているのか，複数の要素（配列）を指しているのかわらかない▶ 複数の要素を指している場合，いくつの要素があるのかわ
からない▶ NULL（データを指していない）の場合もある▶ その結果，危険な操作ができてしまう
Zigでは…
*T ： 単一のT型のデータへのポインタ▶ ポインタの指す先は ptr.* でアクセスする▶ ポインタへの加減算は禁止▶ 必ず1つのデータを指しており，データを指していない状態（null）も許されない
Hiroaki Takada

50

Zxx⾔語によるRTOSの実現

Zigでは… − 続き*[N]T : N個のT型の配列へのポインタ▶ N はコンパイル時に決まる値▶ ポインタの指す先は ptr[i] でアクセスする▶ ReleaseSafeでは，範囲を超えたアクセスが検出される▶ 実は，[N]T が配列で，それへのポインタ[]T : 可変個のT型の配列へのポインタ▶ 実行時に要素数も記憶üC言語であれば，先頭番地と要素数でできた構造体で実現するようなもの▶ ポインタの指す先は ptr[i] でアクセスする▶ ptr.len で要素数を取り出すことができる▶ ReleaseSafeでは，範囲を超えたアクセスが検出される▶ 「スライス」と呼ばれており，実は，ポインタとは別物
Hiroaki Takada

51

Zxx⾔語によるRTOSの実現

Zigでは… − 続き[*]T ： 要素数がわからないT型の配列へのポインタ▶ ポインタの指す先は ptr[i] でアクセスする▶ ポインタへの加減算も可能▶ 範囲を超えたことが検出できないため，危険なポインタ?*T : nullが許される単一のT型のデータへのポインタ▶ ポインタの指す先は，ptr.?.* でアクセスするü? は ptr がnullでない場合にのみ許される
üReleaseSafeでは，ptr がnullの場合が検出される▶ 先頭の ? がnullが許されることを意味し，任意の型の前につけることができる（オプショナル型）▶ nullでないことが保証される（? を使わない）記法もあるünullでない場合に成立するif文やwhile文
Hiroaki Takada

52

Zxx⾔語によるRTOSの実現

Zigでは… − 続き[*c]T ： C言語と互換のポインタ▶ 1つの要素を指しているか，複数の要素（配列）を指しているか，データを指していない（null）か，わからないポインタ▶ （もちろん）危険なポインタ▶ C言語と共存させる場合に用いる[*:x]T : 番兵で終端されたポインタ▶ x は番兵を示す▶ 典型的には，ヌル終端文字列

その他に…
▶ const（Zenでは逆の意味のmut），volatile，アラインメント，
allowzero
Hiroaki Takada

53