ライフタイム
Tockカーネルの値は、以下の3つの方法で割り当てることができます。
-
静的割り当て。静的に割り当てられた値は解放されることはありません。 これらの値はRustでは
'staticライフタイムの「借用」であると表現されます。 -
スタック割り当て。スタック割り当てされた値はレキシカル境界のライフ タイムを持ちます。すなわち、ソースコードを見れば、それがいつ解放されるかがわかります。このような値への参照を作成すると、Rustの型システムは、参照に「ライフタイム」を割り当てることで、値が解放された後ではけっして参照が使用されないことを保証します。
-
グラント値。プロセスのグラント領域から割り当てられた値は、ランタイム 依存のライフタイムを持ちます。たとえば、それがいつ解放されるかは、プロセスがクラッシュするか否かに依存します。Rustの型システムではランタイム依存のライフタイムを表現できないため、Tockでのグラント値への参照は、参照元が所有する
Grant型を介して行われます。
次に、Rust のライフタイムの概念が Tock の値のライフタイムにどのように対応するか、また、これがカーネル内での異なるタイプの値の使用にどのように影響するかについて説明します。
Rustのライフタイム
Rustにおける参照(_借用_と呼ばれる)は、それがどのスコープで有効であるかを決定する その型に関連付けられた_ライフタイム_を持ちます。参照のライフタイムは、借用した値よりも 制限されたものでなければなりません。これにより、コンパイラは参照が有効なスコープから 抜け出せないことを保証します。
その結果、参照を格納するデータ構造体は、その参照の最小ライフタイムを宣言しなければ なりません。たとえば、次のようにします。
#![allow(unused)] fn main() { struct Foo<'a> { bar: &'a Bar } }
これは別の型であるBarへの参照を持つデータ構造体Fooを定義します。この参照は
ライフタイム'aを持ち、これはFooの型パラメータです。'aはライフタイムを示す
名前として、ジェネリックList<E>におけるEのように任意に選んだものであることに
注意してください。また、参照が常に永遠に有効でなければならない場合は、その参照を
含む型(たとえばFoo)のライフタイムに関係なく、型パラメータではなく、明示的な
ライフタイムである'staticを使用することも可能です。
#![allow(unused)] fn main() { struct Foo { bar: &'static Bar } }
バッファ管理
非同期ハードウェア操作で使用されるバッファは静的でなければなりません。ハードウェアが そのポインタを放棄する前にバッファが解放されないことを(ハードウェアに対して)保証する 必要がありますが、一方で、ハードウェアは、自分があるレキシカル境界の中でしかバッファに アクセスしないこと(なぜならハードウェアを非同期的に使用しているので)を我々(すなわち、 Rustコンパイラ)に伝える方法がありません。これを解決するために、ハードウェアに渡される バッファは静的に割り当てる必要があります。
循環依存
Tockはカプセルが互いにアクセスできるようにするために循環依存を使用します。具体的には、
2つの互いに依存しあうカプセルは、それぞれが他方への参照を含むフィールドを持ちます。
たとえば、タイマーAlarmトレイトのクライアントは、タイマーを開始/停止するために
タイマーのインスタンスへの参照を必要とし、タイマーのインスタンスはイベントを伝播する
ためにクライアントへの参照を必要とします。これはプラットフォームの定義でオブジェクトの
作成後にオブジェクトへの接続を可能にするset_client関数により処理されます。
#![allow(unused)] fn main() { impl Foo<'a> { fn set_client(&self, client: &'a Client) { self.client.set(client); } } }