RTAI入門(3)何が出来るか?その3
この記事からの続きです。
RTAIで何が出来るか?の「IPC」編その1です。
- メッセージ
についてまとめます。
なお、RTAI入門(2)のタスクのリアルタイム化は、全ての基礎となっています。ですから、ここだけはご一読をお勧めします。
メッセージ
概要
リアルタイムタスクが複数あるとき、これらをうまく協調動作させないといけません。
そのための仕組みの1つに、「メッセージ」という機構があります。
メッセージとは、タスクからタスクへとデータを運んでくれる仕組みです。
図1 「メッセージ」は、タスクからタスクへデータを運ぶ
運べるデータに特に制限はありません。ですが、だいたい数バイト~数百バイト程度の使用を想定しています。
メッセージとタスク挙動
じつは、メッセージとは単にデータを受け渡しするだけのものではありません。ただデータを受け渡したいだけなら、グローバル変数でも使えば十分です。
メッセージのキモは、データの受け渡しよりもむしろ、それにともなうタスクの挙動の方にあります。
メッセージとタスク挙動は次のようにまとめられます。
- メッセージは、特定のタスクに向けて送信できる
- メッセージを送信するときに・・・
- 受信タスクがメッセージ受け取り準備できていなければ、送信タスクはブロックする
- 受信タスクがメッセージ受け取り準備できていれば、送信タスクはブロックしない
- メッセージを受信するときに・・
- メッセージがどこからも来ていなければ、受信タスクはブロックする
- メッセージがどこかから来ていれば、受信タスクはブロックしない
ここで「ブロック」とは、タスクが一時停止する事をいいます。たとえば某ダム建設が、予算うんぬんでモメて工事止まってますよね。あれも、「工事タスク」がブロックしている状態と言えます。(ダムの場合は、ブロックどころかそのままタスクがKillされそうな勢いですが・・・)
ではここで、もう少し具体的に見てみましょう。
まず、ここにタスクが2つあるとします。タスク1から、タスク2に対してメッセージを送りたいものとしましょう。
これらのタスクは、次のように振る舞います。
- タスク2は、普段は何か別の処理をしている。する処理がなくなってヒマになったら、何かメッセージが来ないか待ちかまえる
「何かメッセージが来ないか待ちかまえる」というのが、「メッセージ受け取り準備」に相当します。RTAIでは、rt_receive というAPIを使用します。
- タスク1は、気が向いたらタスク2にメッセージを渡したがる
「メッセージを渡したがる」というのが、「メッセージ送信」に相当します。RTAIでは、rt_send というAPIを使用します。
この時のパターンとして3つ考えられます。
パターン1
タスク2が準備できてないのに、タスク1がメッセージを渡そうとした
図2 タスク2が準備できてないのに、メッセージを渡そうとした
ここで、(1)と(2)ではそれぞれ次のような会話がなされています。
(1)タスク1がメッセージを渡そうとした
タスク1 「これ、あたらしいメッセージです!読んで下さい!」(= rt_send )
タスク2 「まだ他の仕事してるんだってば!受け取らないよ!?」
タスク1 「スミマセン。準備できるまでずっと待ってます・・・」(=ブロック)
(2)タスク2がやっと準備完了した
タスク2 「えーっと、次のメッセージは・・・」(= rt_receive )
タスク1 「はい!待ってました!ずっと! さぁ、これです!!!」 ( = ブロック解除 )
こんな感じで、メッセージ送信側であるタスク1は、rt_send を実行したとたんブロックします。
そのブロックが解除されるのは、メッセージ受信側であるタスク2が rt_receive を実行したときです。
パターン2
タスク2が準備できてから、メッセージを渡した。かつ、タスク2の方が優先度が高い
パターン2、パターン3では、設定を付け加えます。このシステムは1CPUとします。ですから、タスク1、タスク2のいずれかしか実行できません。
その場合、実行されるのは優先度の高いほうのタスクです。
まずパターン2では、優先度を タスク1 < タスク2 とします。
図3 タスク2の準備が先にできた。そのあとからメッセージをわたした。
(1)タスク2の準備が先に完了した
タスク2「えーっと、次のメッセージは・・・」( = rt_receive )
タスク2「えっ、無いの!?じゃあ、寝て待つか・・・」(=ブロック)
タスク1「よぅし!じゃまなタスク2が居なくなったぞ。さっそく活動開始だっ!」
(2)タスク1がメッセージの準備完了した
タスク1「メッセージ準備できました!」(=rt_send 、ブロックしない)
タスク2「よしきた!これで仕事を続けられるぞ!」(=ブロック解除)
タスク1「ちっ。タスク2が活動を始めたか。これじゃあ動けないな・・・」(=ブロックされてはいないが、動けない)
ここで(2)の段階では、タスク1、タスク2ともにブロック解除されています。
だからどのタスクが動いても不思議ではありません。 しかし、RTAIでは常に優先度の高いタスクがCPUを占有するようになっています。
ですからこの場合は、優先度の高いタスク2が動く事になります。
パターン3
タスク2が準備できてから、原稿を渡した。かつ、タスク1のが優先度が高い
パターン2と似ていますが、優先度を タスク1 > タスク2 とします。
図4 タスク2の準備が先にできた。そのあとからメッセージをわたした。
(1)タスク2の準備が先に完了した
タスク2「えーっと、次のメッセージは・・・」( = rt_receive )
タスク2「えっ、無いの!?じゃあ、寝て待つか・・・」(=ブロック)
(2)タスク1がメッセージの準備完了した
タスク1「メッセージ準備できました!」(=rt_send 、ブロックしない)
タスク2「よしきた!これで仕事を続けられるぞ!」(=ブロック解除)
タスク2「え?タスク1のが優先!?そっか・・・うん、わかった」(=ブロック解除されてはいるが、動けない)
ここで(2)の段階では、タスク1、タスク2、ともにブロック解除されています。
パターン2と違い、最優先なのはタスク1です。そのため、優先度の高いタスク1が動く事になります。タスク2が動けるのは、タスク1が何らかの理由でブロックされたりした時だけです。
まとめ
このように、
rt_send が先か? rt_receive が先か?
送信タスク、受信タスクのどちらが優先度が高いか?
この2つの問題の兼ね合いによって、タスクの挙動が変わります。
さて、これらの挙動はあくまで典型的なものです。以降では、もっといろんな種類のメッセージ送受信についてご紹介します。
条件付きメッセージ送信
上記の例では、メッセージ送信しようとしたときに、次のように振る舞いました。
- 対象タスクが、すでに rt_receive していたら、送信元はブロックしない
- 対象タスクが、まだ rt_receive していなければ、送信元はブロックする
RTAIでは、さらに次のようなメッセージ送信方法が用意されています。
ブロックしないメッセージ送信
送信元がブロックしてもらっては困る場合もあります。その場合には、rt_send_if というAPIを使います。すると次のように振る舞います。
- 対象タスクが、すでに rt_receive していたら、メッセージを渡す。送信元はブロックしない
- 対象タスクが、まだ rt_receive していなければ、メッセージは渡さずにあきらめる。送信元はブロックしない
このように、対象タスクがrt_receive しなければ、さっさとあきらめてしまいます。根性のないやつめ!
決められた時間だけブロックするメッセージ送信
送信元がちょっとブロックする分にはいいけど、ずーっとは困る。そういう場合には、 rt_send_timed というAPIを使います。
- 対象タスクが、すでに rt_receive していたら、メッセージを渡す。送信元はブロックしない
- 対象タスクが、まだ rt_receive していなければ、決められた時間だけブロックする。
それでもまだ対象タスクがrt_recieveしてくれなければ、メッセージは渡さずにあきらめる。そしてブロック解除する。
このように、対象タスクがrt_receiveしてくれるまである程度の時間だけ待機します。それでもダメならあきらめます。
条件付きメッセージ受信
メッセージ送信と同様、メッセージ受信でも条件付き挙動が可能です。
典型的な挙動は、次の通りです。
- どこかのタスクが、すでに rt_send してくれていたら、受信タスクはブロックしない
- どのタスクも、まだ rt_send してくれていなければ、受信タスクはブロックする
RTAIでは、さらに次のようなメッセージ受信方法が用意されています。
ブロックしないメッセージ受信
rt_receive_if というAPIを使うと、次のように振る舞います。
- どこかのタスクが、すでに rt_send してくれていたら、メッセージを受け取り、受信タスクはブロックしない
- どのタスクも、まだ rt_send してくれていなければ、メッセージ受信はあきらめる。受信タスクはブロックしない
このように、メッセージが来ていれば受け取ります。しかし、そうでなければ、さっさとあきらめてしまいます。
決められた時間だけブロックするメッセージ受信
rt_receive_timed というAPIを使うと、こうなります。
- どこかのタスクが、すでに rt_send してくれていたら、メッセージを受け取る。受信タスクはブロックしない
- どこかタスクが、まだ rt_send してくれていなければ、受信タスクは決められた時間だけブロックする。
それでもま誰も rt_recieveしてくれなければ、メッセージ受信はあきらめる。そしてブロック解除する。
このように、どこかのタスクがrt_sendしてくれるまである程度の時間だけ待機します。それでもダメならあきらめます。
次回
次回は、IPC編その2として、メールボックスについて書きます。