(toppers-users 3835) Re: SSPの DEF_EPRI とは何でしょうか。

yasuo kominami(nifty) ykominami @ nifty.com
2012年 1月 27日 (金) 15:57:54 JST


小南です。

こいさんにメールでコメントを求められたのですが、MLに書いたほうがいいと判断したため、こいさんの追伸につけた私のコメントの補足として書きます。

他のスレッドにも言及します。

1.2つの優先度について

toppers-users 3795、3818での、アライブビジョンソフトウエアの高橋さんの指摘は、2つの優先度があることが原因というよりは、各タスクの優先度(起動時優先度)毎にタスクを1つのみ割り当てるということの影響の方が強いためだと思います。

このケースで、起動時優先度のみを指定したとしても、同じことが起こります。
またTOPPERS/ASP,JSPなどでも同じです。


高橋さんの挙げられた例は、優先順位の点から見ると、実行時優先度を指定すると、見かけ上TOPPERS/ASPなどで同一優先度に複数タスクを指定した場合と同じになります。
ただし、TOPPERS/SSPの場合、同一の実行時優先度のキューの並び順は(動的に定まる)FCFSではなく、(静的に定まる)起動時優先度順であるため、高橋さんの指摘されたことが発生しえます。
「見かけ」は同じですが、挙動は異なります。


SSPの実行時優先度を指定しなければ(起動時優先度のみを指定すれば)、「まずいことが起こりそうだ」と感じることが出来るでしょうが、ここでSSPの実行時優先度を指定することで、ASPなどと同じ挙動をすると勘違いしやすくなる(見えにくくなる)ことが高橋さんが「使えない」と考えられる理由ではないかと思います。

カーネルがタスクのスケジューリングに用いるタスクの優先度は、全体の状態の変化に合わせて、次に実行状態にすべきタスクを選び出すときに用いるものです。

逆に、タスクの優先度を用いて、タスクを指定した(あるいは希望する)順番に順次実行させる、ということは、結構難しいです。
排他制御も、(排他制御したい対象に関わる)競合するものの間で、実行する順番を決めたいということなので、同様に難しいです。

なぜなら、少数のタスクの集まりに対して、そのタスクの集まりの中での実行する順番を決めるたいのに、他のタスクなどの状態を考慮するカーネルに順番を決めさせると、そのタスクの集まりの中では考慮する必要がないことまでも考慮して(いわば余計なおせっかいをして)決めるため、(常に)希望した順に実行してくれるとは限らないとなってしまいます。

まあこれは、タスクの優先度のみを用いて実行する順番を決めたいとした場合のことで、TOPPERS/ASPなどの場合は、同一優先度のタスクが複数あり、その中での実行順序を決めたい場合は、(それらのタスクを必ずカーネルが見つけ出してくれるため、)FCFSであることを考慮すれば、比較的望み通りに実行順序を決めやすくなります。



逆に、設計者は、優先度が異なるタスクに対しては、それぞれ別の機能を割り当てるべきだと思います。
斉藤さんのログタスクの例も、高橋さんの指摘された例も、一つのタスクにまとめてしまい、その中で順序を制御することの方が簡単に実現できるし、問題も発生しないと思います。
なぜなら、タスク間で順序を指定することは難しいですが、タスク内(C言語の関数内)では当たり前にできるからです。


1つのタスクに抱える機能が増えるのをとるか、タスク間での実行順序を指定しやすくする(カーネルの実装の増加)をとるのかのトレードオフの問題ともいえます。


実行時優先度は、仕様に対する誤解を招きやすい点を除けば、あまり突飛なものではないです。
逆にあまり機能を改善するものでもないのに、誤解を招きやすいのは問題だともいえます。

タスクが実行状態になったときに、タスクの優先度は実行時優先度になりますが、タスクの実行自体に、優先度は関係ありません。
優先順位に関わる状態が変化するとき、状態変化に関わるのは、(従来の優先度と同じように扱える)起動時優先度であり、変化
後の優先順位に対して、実行状態のタスクを変更すべきか、否かを判断するためにだけ、実行時優先度は用いられます。
仕様や実装をASPと比較してもあまり変わらないです。逆にいうと改善もされていません。
しかし、誤解されやすいです。



それから、SSPの開発者の認識では、実行時優先度は、排他制御などに利用するもののようです。
その意図であれば、実行時優先度を指定した場合に、起動時優先度に対して優先度逆転が発生しているようにも
確かに1優先度に1タスクのみという制約のもとでは、実行時優先度でも(簡単な)排他制御は出来そうです。
ただ、排他制御は「待ち」を用いるほうが自然だし、「待ち」をなくしたのならば、排他制御を用いない設計を目指すべきではないかと考えます。




2.SSPでタスクがプリエンプトされても、スタックを共有できるわけ

toppers-users 3737
において、μITRON4.0の仕様の制約タスクについて述べましたが、あらためてSSPのアーカイブに同梱されているドキュメントを確認したところ、SSPは制約タスクのみをサポートし、CRE_TSKにはスタック領域のアドレスを必ずNULLに指定することとなっていました。
したがって、SSPでは必ず全てのタスクでスタックが共有されることになります。
そこで、SSPのソースを確認したうえで、以下にコメントします。



SSPでは待ちがないため、あるタスクが実行状態になった場合、そのタスクよりも起動時優先度が高いタスクは実行できる状態になく、かつ存在するとすれば、休止状態です。

そのため、自身が呼び出されたrun_task()内でのスタックポインタの位置より上は、自由に使っていいことになります。

逆にそのスタックポインタより下は、run_task()を呼び出したタスクが使っていた状態が残っています。
呼び出したタスクの起動時優先度は、自身の起動時優先度よりも低いです。


run_task()はact_tsk()、iact_tsk()などから呼び出されます。
run_task()が呼び出されるのは、それを呼び出した実行状態のタスクの実行時優先度よりも、高い起動時優先度を持つタスクに切り替わるときです。
切り替った先のタスクは、タスクのメインルーチンの先頭から実行を開始します。

タスクがメインルーチンからリターンしたり、ext_tsk()を読んだりして終了してrun_task()内の戻ってきた場合、スタックはタスクのメインルーチンを呼び出したときの位置になっています。
run_task()内で、run_task()を呼び出したタスクの起動時優先度よりも高い起動時優先度を持つタスクが実行可能状態であるか調べ、実行可能状態であれば、そのタスクのメインルーチンを呼び出します。

そうでなければ、run_task()からリターンします。
run_task()からリターンしたところは、run_task()を呼び出したタスクになります。
このとき、このタスクよりも起動時優先度が高いタスクは実行できる状態になく、かつ存在するとすれば、休止状態です。