Rust標準ライブラリ

現行版のRustの標準ライブラリにはSOLIDターゲットのサポートが含まれています。本セクションでは標準ライブラリの各APIの対応状況ならびにそれらがどのSOLID-OS向けにどのようにして実装されているかを説明し、SOLIDターゲット固有の注意点を挙げます。

Rust標準ライブラリのAPIドキュメントはこちらからご覧になれます。

注意

RustはRust Foundationとコミュニティによって活発に開発されているオープンソースソフトウェアです。APIの安定性は保証されていますが、実装は予告なしに変化する可能性があります。実装詳細に関する強い保証が必要な場合、カーネルAPIを直接使用することが推奨されます。

主な注意事項

ベアメタル環境でも使用できる core, alloc に関しては特記すべき事項はありません。 std が提供するOS抽象化APIに関しては、特に他のターゲットと比較して以下の点に関して注意を要します。

  • 同期プリミティブスレッド操作APIはカーネル資源を動的生成することで実現されています。標準ライブラリ自体もいくつかの資源を必要とし、また必要になった特に初めて動的生成を行います。資源の動的生成に失敗した場合はパニック、それが不可能な場合はアボートします。

  • ファイルシステムAPIはスレッド安全ではありません。この問題は近日中のツールチェーンアップデートで解消予定です。

  • Rustの他の多くのターゲットとは異なり、TOPPERSカーネルはRTOSカーネルとしては一般的な優先度ベーススケジューリングを使用しています。あるタスクを実行中しているとき、明示的に他のタスクに実行権を譲らない限りは、実行権が譲られることはありません。

  • RTOSカーネル固有のシステム状態では標準ライブラリの同期プリミティブ等は使用できません

  • サブプロセス等のSOLID-OSに存在しない概念に関係するAPIは使用できず、実行時エラーを返します。 環境変数はlibc関数を使用しますが、これらのlibc関数はexeGCCでサポートされていないため、アプリケーションで定義することが推奨されます。

  • HashMap, HashSet乱数生成APIを使用しますが、これにはBSPによるサポートが必要です。


スレッド

スレッド操作API (std::thread) はASP3ABI (TOPPERS APIをベースとしたカーネル非依存ABI) の上で実装されています。

スレッドの生成 (std::thread::spawn, std::thread::Builder::spawn) は acre_tsk を使用してタスクを動的生成することで行われます。動的割当て可能なタスクIDが枯渇した場合、 E_ENOID に対応する std::io:Error が返ります。

タスクIDはスレッドが合流 (std::thread::Thread::join) されたとき、あるいはデタッチ (std::thread::Thread をドロップ) 後にスレッドのエントリポイントが制御を返したときに解放されます。合流もデタッチもされなかった場合、タスクIDが解放される保証はありません。

生成されたタスクは生成元タスクのベース優先度を引き継ぎます。この優先度は chg_pri により生成後に変更することができます。

TOPPERS/FMP3では生成されたタスクは呼出し元プロセッサと同じプロセッサに割り付けられます。割付け対象プロセッサは mig_tsk により生成後に変更することができます。

スレッド操作APIによるタスクの管理に干渉した場合 (例えば、タスクを強制終了したり、完了したタスクを再起動した場合) の動作は未定義です。

std::thread::yield_nowrot_rdq(TPRI_SELF) として実装されています。

注意

TOPPERSカーネルは優先度ベーススケジューリングを使用しています。あるタスクがCPU時間を占拠しているとき、std::thread::yield_now を呼び出すか待ち状態に入らない限りは他の同優先度タスクは実行されず、また低優先度タスクは std::thread::yield_now を呼び出しても実行されません。

デフォルトのスタックサイズは64KiB (32ビットターゲット)または128KiB (64ビットターゲット)です。RUST_MIN_STACK 環境変数を設定することで変更することができます。


TLS (スレッドローカル格納域)

TLSはELF TLS (.tdata, .tbss セクション) によりサポートされています。


システムエラー

システムエラー型 (std::io::Error) はSOLID-OS Core ServiceやRTOSカーネルから返されたITRON、SOLID=OS、およびSOLID Socketsエラーコードを表現するのに使用されます。

動的割当て可能なカーネルオブジェクトIDが枯渇した場合のエラー E_ENOIDErrorKind::OutOfMemory にマッピングされます。


同期プリミティブ

ミューテックス (std::sync::Mutex) はTOPPERSカーネルが提供するミューテックス機能で実装されています。常に優先度継承プロトコル (SOLID-OS拡張) を使用します。ミューテックスは acre_mtx で動的生成され、動的割当て可能なミューテックスIDが枯渇した場合はパニックします。

Readers/Writerロック (std::sync::RwLock) はSOLID-OSカーネル拡張で提供されるReaders/Writerロックを使用して実装されます。現時点では初回使用時に rwl_acre_rwl で動的生成され、動的割当て可能なReaders/WriterロックIDが枯渇した場合はパニックします。

注意

std はパニック機構や環境変数アクセスの排他制御のためにミューテックスやReaders/Writerロックを内部で使用します。これらは必要になった場合に動的生成され、失敗した場合はアボートします。このため、ミューテックスとReaders/Writerロックは動的割当て可能なIDを余分に確保しておくことが推奨されます。

std が内部で使用するために動的生成する同期オブジェクトを解放する方法はありませんが、生成される数には上限があります。

条件変数 (std::sync::Condvar) はTOPPERSカーネルではサポートされていないため、std 内で待ち行列を実装することで実現しています。待ち行列はタスク優先度順で処理されますが、タスク優先度の変化には対応できません。

注意

std やサードパーティーパッケージで提供される同期プリミティブの多くは std::sync::Condvar の上で実装されています。

std の同期プリミティブを使用して待ち状態に入ったタスクを強制終了 (ter_tsk) した場合の動作は未定義です。強制待ち解除 (rel_wai) した場合、対象タスクでパニックまたはアボートが発生する可能性があります。

std の同期プリミティブは起床要求キューイング数を変化させる可能性があります。アプリケーションコードが wup_tsk を呼び出して余分に起床要求を行うことは許されます。


動的メモリ割り当て

動的メモリ割り当てはlibcの malloc 関数等を使用して実装されています。


カーネルAPIとのインタラクション

std は汎用システム向けOSのアプリケーションを主なターゲットとしており、CPUロックやディスパッチ禁止状態のようなコンセプトは持っていませんが、SOLIDターゲット向けの内部実装でディスパッチ禁止状態を使用しています。

CPUロック有効状態、ディスパッチ禁止状態、および非タスクコンテクストでは std の同期プリミティブやスレッド管理APIは使用できず、パニックまたはアボートに繋がります。カーネル非動作状態で使用した場合の動作は未定義です。

注意

アプリケーションがプラットフォーム固有のAPIを介して std の動作と干渉した場合の動作は、一般に定義されません。RTOSカーネルに固有のメカニズムを介してタスクの実行に干渉された場合に予測可能な動作が望まれる場合 (例えば、 rel_wai により待ち操作が中断されるケースを処理したい場合)、 std の同期プリミティブを使用せず、カーネルAPIを直接使用することが必要となります。


環境変数

環境変数を扱うAPI (std::env::var 等) はlibc の getenv 等を使用して実装されています。 std 含む様々なライブラリが環境変数を使用しますが、これらのlibc関数はexeGCCではサポートされていません (ライブラリの仕様(ARM) および ライブラリの仕様(AArch64) を参照)。このため、必要な場合はアプリケーションで getenv を定義することを推奨します。

use std::os::raw::c_char;

#[no_mangle]
unsafe extern "C" fn getenv(name: *const c_char) -> *const c_char {
    let name = std::ffi::CStr::from_ptr(name);
    let value = match name.to_bytes() {
        // `std::thread` のデフォルトスタックサイズを減らす
        b"RUST_MIN_STACK" => b"32768\0",
        _ => return std::ptr::null(),
    };
    value.as_ptr() as _
}

std::collections::{HashMap, HashSet}

HashMapHashSet のデフォルト Hasher 実装である std::collections::hash_map::RandomState はHash DoS耐性を確保するために乱数生成器を使用して内部状態を乱数化します。乱数を生成するために SOLID_RNG_SampleRandomBytes() 関数を使用します。この関数が失敗した場合はパニックします。

注釈

SOLID_RNG_SampleRandomBytes() による乱数生成にはBSPまたはアプリケーションによるエントロピー源デバイスドライバの実装が必要です。


時刻

実時刻 (std::time::SystemTime) はlibcの time_t のラッパーです。現在時刻の取得はSOLID-OS RTC APIを使用して行われます。タイムゾーンはサポートされておらず、 SOLID_RTC_ReadTime() から返された SOLID_RTC_TIME はUTCとして解釈されて time_t に変換されます。

計測用の時刻 (std::time::Instant) はTOPPERS APIの get_tim を使用して実装されています。


標準入出力

標準出力と標準エラー出力は SOLID_LOG_write() に接続されています。標準入力は実装されておらず、 read メソッドは常に Ok(0) を返します。


ファイルシステム

ファイルシステムAPI (std::fs) はSOLID-OSファイルシステムAPIの上で実装されています。パスAPI (std::path) はSOLID-OSファイルシステムで使用するために \ (ASCII: バックスラッシュ, Windows-932: 円記号) をパス区切り文字として使用します。

以下のAPIは対応するSOLID-OSファイルシステムの機能が無いため実装されておらず、 ErrorKind::Unsupported エラーを返します。

  • std::fs::File::file_attr: 開かれたファイルハンドルからファイル属性を取得する

  • std::fs::File::truncate: 開かれたファイルの末尾を切り捨てる

  • std::fs::File::duplicate: ファイルハンドルを複製する

  • std::fs::File::set_permissions: 開かれたファイルハンドルのファイル属性を設定する

  • std::fs::symlink: シンボリックリンクを作成する

  • std::fs::link: ハードリンクを作成する

  • std::fs::canonicalize: パスを正規化する

  • std::env::[set_]current_dir: カレントディレクトリの操作

注意

SOLID-Rustの現時点のデフォルトツールチェーンに含まれる標準ライブラリ実装ではSOLID-OSファイルシステム操作はスレッド安全ではありません。この問題は近日中のツールチェーンアップデートで解消予定です。

UNIXターゲットと同様にパスおよびOS文字列 (std::ffi::OsStr) は未解釈のバイト列として表現されます。 str への変換はバイト列をUTF-8として解釈することで行われます。

注釈

OS文字列とバイト列の間の無損失変換は std::os::solid::ffi::{OsStrExt, OsStringExt} を使用することで実現できます。このAPIはUNIXターゲット向けのものと同等です。


ネットワーク

ネットワークAPI (std::net) はSOLID Sockets APIの上で実装されています。サポートされる機能はSOLID Sockets実装によります。例えば、LwIPソケットは try_clone 操作 (SOLID_NET_Dup) をサポートしていません。


パニック

サポートされています。

注意

パニック時のアンワインド (ローカル変数のデストラクタの実行と std::panic::catch_unwind による捕捉) はC++例外処理機構を使用して実装されています。正しく動作させるためには、 __register_frame_info の呼出しによってC++例外ハンドラ情報が正しく登録されること必要です。


サブプロセス

サポートされていません。


バックトレース

サポートされていません。


スタートアップコード

Rustコンパイラから実行可能ファイルを直接生成する場合、 std に含まれるスタートアップコードが main 関数の前に実行されます。SOLIDターゲットでは実行可能ファイルを直接生成することはサポートしていないため、スタートアップコードが使用されることはありません。