アクター

Lambda OS の各 VM タスクは、自分のスタック・グローバル領域・ヒープを持つ自己完結した Lambda C VM インスタンスです。どのタスクもアクターで、状態を隔離し、メッセージで通信し、コアに固定されます。その隣では、ネットワークスタックや制御ループといった ネイティブの Zig タスクが走ります。

ノードの役割

ノードには役割があります。

オーケストレータノード(x86_64 / riscv64 / aarch64)は、多数のアクターを抱えます。実行時にアクターを生成し、監視し、死ねば作り直します。

エッジノード(MMU 無しの MCU)は、1〜2 個のアクターを持ち、ネットワークで協調します。

**多数のアクターを 1 チップで抱えて管理する機構は、設計上、オーケストレータにしか置きません。**264 KB の部品にそれを載せるのは、その機構が何も生まない場所で回すことであり、分散アクターがそもそも避けるために存在する当のものです。

アクター API

型付きメールボックスと send_remote は全ターゲットにあります。残りは対応範囲が分かれます。

x86_64riscv64aarch64MMU 無し MCU
send / recv / msg_from / msg_a / msg_b
msg_tag
send_remote
spawn(動的生成)
monitor / die(監視)
register_name / whereis(ローカル名前解決)
has_msg(非ブロッキング peek)
node_ip / send_node / reply_node

**型付き要求/応答。**メッセージは {from, tag, a, b} です。send(pid, tag, a, b) は送信者の pid を自動で刻むので、ハンドラは誰が呼んだかを知らずに send(msg_from(), …) で答えられます。recv() は dispatch 用に tag を返します。

動的生成と監視(オーケストレータ)。spawn(role) が、走行中の FFI の中から実行時に新しいアクターを作ります。monitor(pid) はアクターが死んだときに知らせるよう要求し、die() は予約タグ MSG_DOWN でモニタに通知して終了します。MSG_DOWN を受けたスーパバイザは、空いたスロットにワーカーを作り直します。こうして、システムは自分で立て直します。

**エッジノードには、これがありません。**その代わり、フレーム単位の障害復帰があります。

実機(Pico W)で、飛行中にフォルトを注入して確認済みです。ミッションアクターが兄弟のコンテキストへ書き込み、MPU がそれを拒否し、カーネルがフレームをスキップし、配送ミッションは完走しました。制御ループも兄弟アクターも、それに気づきません。

ノードを跨いだ監視(別ノードの監視役がエッジノードの死を検知して再供給する)は、未実装です。

フレーム駆動の実行

スケジューラは、各スクリプトの main() を毎 tick 呼び直します。グローバル領域の値は呼び出しをまたいで残り、オペランドスタックは毎フレーム捨てられます。だからスクリプトは、内部に無限ループを書きません。1 フレームで一区切りの仕事をして、返します。

フレームが、協調的な進行の単位です。そして行儀の悪いフレームが機械を固めないことを、プリエンプトするタイマが保証します。加えて x86・riscv64・aarch64・MMU 無し MCU では、タスク毎の命令数ウォッチドッグが 1 フレームを有界化します。

グローバルロックの無い並行性

VM は、プロセス全体の実行状態を、ふつうのグローバル変数に持ちます。VM の実行を 1 つのロックで直列化する代わりに、スケジューラが、コンテキストスイッチのたびに、その実行状態をタスクごとに退避・復元します

これが大きく効きます。**VM のフレームは、その周りに割り込み禁止区間を持たず、完全にプリエンプトできます。**割り込みをマスクする区間は、本当に共有されるデータ——アクターのメールボックス、コンソール、NIC の送信経路、タスク生成——を守る、短いものだけ。あとはタスクごとのスタック・グローバル領域・ヒープが、競合を防ぎます。

SMP では、コアごとの VM グローバルを、コアごとのベースアドレスで切り分けます。x86 は GS ベース、riscv64 はスレッドポインタ、aarch64 は TPIDRRO_EL0、RP2040 の 2 コアはコア ID レジスタ。この 4 つでは、VM が全コアで走ります。RP2350 はまだ 1 つのブロックを共有しているので、VM は単一コアのままです。

位置透過:機械を跨ぐメッセージ

リモートのメッセージが、ローカルのメールボックスに届き、ローカルのものと同じ recv() で読めます。

実機で。Raspberry Pi Pico 2 W(RP2350、MMU 無し)で走る非特権・PMP 隔離のアクターが、実 Wi-Fi・実 LAN 越しに、x86 の Lambda OS で走るアクターへ型付きメッセージを送ります。相手はそれをふつうのメールボックスで受けて返信し、その返値が、送った側のアクター自身のメールボックスに戻ってきます。

そのイメージでは、隔離されたアクターの隣でネットワークスタックが特権タスクとして走っています。隔離と分散が、同じイメージで両立しています。

透過性がどこまでか

**受信経路は透過的です。**リモートのメッセージは、ローカルのものと同じメールボックス、同じ recv()、同じ tag 分岐で処理されます。

**返信経路は透過的ではありません。送信元が別ノードなので、msg_from()-1 を返します。**したがって、ふつうの send(msg_from(), …) で返すことができません。返信は reply_node() を通ります。カーネルが受信パケットから記録した返信先を使います。

**リモートに答えるスクリプトは、それと承知で書くことになります。**位置透過は、受信については完全で、返信については部分的です。

実測されたこと

ノードを跨ぐマイクロ秒協調:未実装

アクターのメッセージ配送はベストエフォートです。ノードを跨いで共通のクロックで動くこと(隊形を保つスワームのような、真にマイクロ秒協調した動作)は実装していません

それには共有の時間基準(有線なら PTP、無線ならノード毎の GPS/PPS 規律)と、ローカルの時刻駆動実行が要ります。エミュレーションでは検証できません。2 つのゲストは、各々がホストに独立に乱されるため、共通のクロックを保てないからです。