RTAI入門(3)何が出来るか?その3

この記事からの続きです。

RTAIで何が出来るか?の「IPC」編その1です。

  • メッセージ

についてまとめます。

なお、RTAI入門(2)のタスクのリアルタイム化は、全ての基礎となっています。ですから、ここだけはご一読をお勧めします。

メッセージ

概要

リアルタイムタスクが複数あるとき、これらをうまく協調動作させないといけません。

そのための仕組みの1つに、「メッセージ」という機構があります。

メッセージとは、タスクからタスクへとデータを運んでくれる仕組みです。

howto_rtai_03_00図1 「メッセージ」は、タスクからタスクへデータを運ぶ

運べるデータに特に制限はありません。ですが、だいたい数バイト~数百バイト程度の使用を想定しています。

メッセージとタスク挙動

じつは、メッセージとは単にデータを受け渡しするだけのものではありません。ただデータを受け渡したいだけなら、グローバル変数でも使えば十分です。

メッセージのキモは、データの受け渡しよりもむしろ、それにともなうタスクの挙動の方にあります

メッセージとタスク挙動は次のようにまとめられます。

  • メッセージは、特定のタスクに向けて送信できる
  • メッセージを送信するときに・・・
    • 受信タスクがメッセージ受け取り準備できていなければ、送信タスクはブロックする
    • 受信タスクがメッセージ受け取り準備できていれば、送信タスクはブロックしない
  • メッセージを受信するときに・・
    • メッセージがどこからも来ていなければ、受信タスクはブロックする
    • メッセージがどこかから来ていれば、受信タスクはブロックしない

ここで「ブロック」とは、タスクが一時停止する事をいいます。たとえば某ダム建設が、予算うんぬんでモメて工事止まってますよね。あれも、「工事タスク」がブロックしている状態と言えます。(ダムの場合は、ブロックどころかそのままタスクがKillされそうな勢いですが・・・)

ではここで、もう少し具体的に見てみましょう。

まず、ここにタスクが2つあるとします。タスク1から、タスク2に対してメッセージを送りたいものとしましょう。

これらのタスクは、次のように振る舞います。

  • タスク2は、普段は何か別の処理をしている。する処理がなくなってヒマになったら、何かメッセージが来ないか待ちかまえる

「何かメッセージが来ないか待ちかまえる」というのが、「メッセージ受け取り準備」に相当します。RTAIでは、rt_receive というAPIを使用します。

  • タスク1は、気が向いたらタスク2にメッセージを渡したがる

「メッセージを渡したがる」というのが、「メッセージ送信」に相当します。RTAIでは、rt_send というAPIを使用します。

この時のパターンとして3つ考えられます。

パターン1

タスク2が準備できてないのに、タスク1がメッセージを渡そうとした

howto_rtai_03_01

図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 とします。

howto_rtai_03_02

図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 とします。

howto_rtai_03_03

図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として、メールボックスについて書きます。