南角先生の組込み講座

【設計しながら学ぶRTOSの基本編】第2回 タスクの並行性

こんにちは、南角 です。
新年おめでとうございます。今年もよろしくお願いします。といってもこれが掲載される頃は、新年ははるかに過ぎていますが。

さて今回はまず、並行性を実現するために重要なデータであるTCB(Task Control Block)の設計を行うための前提を説明します。
前にも書いたことはあると思いますが、タスクと優先度付きのISR(Interrupt Service Routine)は似た面もあります。
図1は多重割り込みを許可した場合のISRの動作例を示したものです。


図1 多重割り込みを許可した場合のISRの動き

上向きの矢印が割り込みの発生を示し、四角形がISRの実行を示します。
タスクと似ていますよね。でも大きな違いがあります。それは、ISRは入れ子の順番でしか実行できないということです。
もう少し詳しく説明します。多重割り込み方式ではすべてのISRが一つのスタックを共有しており、ISRは新たな割り込みが発生してプリエンプトされると、実行を再開するためのデータである実行環境データがスタックに保存されます。
スタックの使用順序は、割り込みの発生順という外的要因で決まるため、実行環境データの保存場所も毎回変化します。
そのためスタックに保存された逆順でしかデータを復元できず、ISRはプリエンプトされた逆の順番でしか再開させられません。
そのためISRは入れ子の順番でしか実行できないわけです。
一方タスクの場合は待ち状態が発生します。たとえばタスクがセマフォを使用する場合を考えてみます。
セマフォが他のタスクによりロックされている時にセマフォ獲得要求操作をすると、タスクは実行を一時停止させられます。
停止したタスクの実行再開は、他のタスクのセマフォ解放操作によってなされるため、再開の順番はタスクが一時停止させられた順番とは無関係となります。
これを実現するためには実行環境の保存場所がタスクごとに定まっていなければなりません。
これがTCBです。TCBを持つことによって個々のタスクは独立して実行できることになります。もっと具体的にいうと、他のタスクとは無関係にタスク内であればどこでも一時停止して、再開できるような “状態”を持つことができます。
タスクがどのような状態を持つかはOSによって異なりますが、ほとんどのOSは“実行中”(running)、“実行可能”(ready)、“待ち”(waiting、VxWorksではpendedとよぶ)の3つの状態を持っています。図2にタスクの状態遷移図を示します。
上で説明した以外に“休止”(dormant)という状態もありますが、OSはタスクとして認識はしているがまだ実行されていない状態、またはまだタスクにもなっていない状態でOSの操作対象外のものを示します。
下の状態遷移図における、状態遷移を起こす操作に関する説明は省略します。考えてみてください。
なお、RunningからRunningになっている状態遷移がありますが、これは実行中のタスクがそのまま実行を続けるシステムコールもあるということを示すためにあえて記述しました。


図2 タスクの状態遷移

タスクが独立して実行できるためには、タスクが独自に使用できるメモリ - ローカルメモリも必要です。
これはスタックに取られます。つまりタスク個別のスタックも必要ということになります。
個々のタスクの命令(mainに相当するものや個々の関数)はリエントラント(reentrant - 日本語でいうと再入可能)でなければなりません。リエントラントとは同時実行可能な関数ということです。具体的には実行中に一時停止させられて、再度自分が呼び出されても正しく実行され、さらにその後一時停止させられた残りの部分を再開させられても、そちらも正しく実行されるような構造の関数ということです。リカーシブ(recursive - 再帰)と似ていますがリエントラントの方が少し条件が甘いです。リカーシブは関数ごとに異なる関数のローカル変数のみを使用しなければなりませんが、リエントラントは排他制御をしっかりしておけばグローバル変数の使用も許されます。

TCBにはこれらの情報もCPUのレジスタ情報などとともに保存しなければなりません。
次回は具体的なTCBの設計に入りたいと思います。

設計しながら学ぶRTOSの基本編 第2回 おわり