めのん@ひとりプログラミング同好会

このブログはすべてフィクションであり、実在の人物、団体とは一切関係ありません

Zig版ASP3カーネルのTINIBとTCB

おはようございます、めのんです!

前回までで静的APIのCRE_TSKについては一通り目を通しました。 他の静的APIについてもほぼ同じようなことの繰り返しですので、静的APIについてはいったんこれで終わりにします。

今回はCRE_TSKで生成していたタスク初期化ブロックTINIBと、それと密接に関連するタスク管理ブロックTCBを見ていきたいと思います。 TINIBについては前回見たtask.cre_tsk関数が返していた型ですね。

TINIBとTCBって実はタスクの実態そのものだと思うんです。 この2つの構造体が分離されているのは実装都合で、本当は一緒に見るべきだと思うんですよね。

どういうことかというと、TINIBはプログラムの実行中ずっと値が変わらない情報で、普通はROMに配置されます。 一方でTCBはそのときどきの状態を管理するものなので頻繁に値が変更されます。

やや遅くなりましたが、TINIBとTCBの定義を見てみましょう。 どちらもkernel/task.zigに定義があります。

///
///  タスク初期化ブロック
///
///  タスクに関する情報を,値が変わらないためにROMに置ける部分(タスク
///  初期化ブロック)と,値が変化するためにRAMに置かなければならない部
///  分(タスク管理ブロック,TCB)に分離し,TCB内に対応するタスク初期
///  化ブロックを指すポインタを入れる.タスク初期化ブロック内に対応す
///  るTCBを指すポインタを入れる方法の方が,RAMの節約の観点からは望ま
///  しいが,実行効率が悪くなるために採用していない.他のオブジェクト
///  についても同様に扱う.
///
pub const TINIB = struct {
    tskatr: ATR,                // タスク属性
    exinf: EXINF,               // タスクの拡張情報
    task: TASK,                 // タスクの起動番地
    ipri: c_uint,               // タスクの起動時優先度(内部表現)
    tskinictxb:                 // タスク初期化コンテキストブロック
        if (@hasDecl(target_impl, "TSKINICTXB"))
            target_impl.TSKINICTXB
        else struct {
            stksz: usize,       // スタック領域のサイズ(丸めた値)
            stk: [*]u8,         // スタック領域
        },
};

///
///  タスク管理ブロック(TCB)
///
///  ASPカーネルでは,強制待ち要求ネスト数の最大値(TMAX_SUSCNT)が1に
///  固定されているので,強制待ち要求ネスト数(suscnt)は必要ない.
///
///  TCBのいくつかのフィールドは,特定のタスク状態でのみ有効な値を保持
///  し,それ以外の場合は値が保証されない(よって,参照してはならない).
///  各フィールドが有効な値を保持する条件は次の通り.
///
///  ・初期化後は常に有効:
///         p_tinib,tstat,actque, staovr, leftotm
///  ・休止状態以外で有効(休止状態では初期値になっている):
///         bpriority,priority,wupque,raster,enater,p_lastmtx
///  ・待ち状態(二重待ち状態を含む)で有効:
///         p_winfo
///  ・実行できる状態と同期・通信オブジェクトに対する待ち状態で有効:
///         task_queue
///  ・実行可能状態,待ち状態,強制待ち状態,二重待ち状態で有効:
///         tskctxb
///
pub const TCB = struct {
    task_queue: queue.Queue,    // タスクキュー
    p_tinib: *const TINIB,      // 初期化ブロックへのポインタ

    tstat: u8,                  // タスク状態(内部表現)
    bprio: TaskPrio,            // ベース優先度(内部表現)
    prio: TaskPrio,             // 現在の優先度(内部表現)
    flags: packed struct {
        actque: u1,             // 起動要求キューイング
        wupque: u1,             // 起床要求キューイング
        raster: bool,           // タスク終了要求状態
        enater: bool,           // タスク終了許可状態
        staovr: if (TOPPERS_SUPPORT_OVRHDR) bool else void,
                                // オーバランハンドラ動作状態
    },
    p_winfo: *WINFO,            // 待ち情報ブロックへのポインタ
    p_lastmtx: ?*mutex.MTXCB,   // 最後にロックしたミューテックス */
    leftotm: if (TOPPERS_SUPPORT_OVRHDR) PRCTIM else void,
                                // 残りプロセッサ時間
    tskctxb: target_impl.TSKCTXB,
};                              // タスクコンテキストブロック

このうちTINIBについてはtask.cre_tskのときに見ていった内容でいいと思いますので、ここではTCBに注目することにします。

TCBの最初の方にp_tinibというのがありますね。 これはTINIBへのポインタで、TCBがあれば同じタスクのTINIBも参照できるようになっています。

その上のtask_queueは線形リストのノードにするための構造体で、レディキューに登録するためのものだと思います。 レディキューというのはタスク優先度ごとにあって、実行可能状態のタスクが優先順位に従って並んでいる待ち行列です。 レディキューの定義もすぐ下にありました。

///
///  レディキュー
///
///  レディキューは,実行できる状態のタスクを管理するためのキューであ
///  る.実行状態のタスクも管理しているため,レディ(実行可能)キュー
///  という名称は正確ではないが,レディキューという名称が定着している
///  ため,この名称で呼ぶことにする.
///
///  レディキューは,優先度ごとのタスクキューで構成されている.タスク
///  のTCBは,該当する優先度のキューに登録される.
///
pub var ready_queue: [TNUM_TPRI]queue.Queue = undefined;

続いてflagsですが、これはpacked structという一種の構造体ですね。 packed structだと、フィールドの順が保証されている、パディングがない、bool型は必ず1ビットになるなどの特徴があります。 u1型という1ビット符号無し整数型もZigらしいですね。

そのあとに続くp_winfoは待ち情報ブロックへのポインタです。 これはタスクが待ち状態のときに使うようで、主にタイムアウト監視のために使うようです。 待ち状態のタスクはtask_queueをレディキューではなくタスク優先度準の待ちキューにつなぐようです。 これについては別の機会に見ていけたらと考えています。

p_lastmtxはミューテックスの優先度継承で使うのでしょうか? よくわかっていません。

leftotmはオーバーランハンドラで使う情報ですね。 オーバーランハンドラを使う場合以外はvoid型になるようです。

tskctxbはタスクコンテキストブロックで、タスクのスタックポインタやプログラムカウンタを退避する領域です。

ざっとTINIBとTCBを読んでみましたが、次回はタスクに関連するこまごました内容を見ていきたいと思います。

それでは!!