RTAI入門(5)何が出来るか?その5
この記事からの続きです。
RTAIで何が出来るか?の「IPC」編その3です。
- 共有メモリ
- FIFO
- セマフォ
についてまとめます。
なお、共有メモリ、FIFO、セマフォは、カーネルモードのアプリを作る時にうれしいものです。
一方、今回のシリーズはユーザーモードのアプリを主眼に考えています。ですから、今回は軽く流します。
なお、RTAI入門(2)のタスクのリアルタイム化は、全ての基礎となっています。ですから、ここだけはご一読をお勧めします。
共有メモリ
共有メモリを使用すると、リアルタイムタスク間でのデータ共有が可能です。ただし、ユーザーモードのアプリから共有メモリを使用すると、リアルタイム性が失われます。ですから、これはカーネルモードにて使用する際に使うのが良いようです。
さて、共有メモリとはメモリです。(ん?)
メモリを確保するには、Linuxカーネルに処理を依頼しないといけません。しかし、Linuxカーネルそのものはリアルタイム性を持っていません。そこで、次のような方法で共有メモリを実現します。
図1 共有メモリは、「ざっくり山」方式
まず、リアルタイムアプリが起動した際に、ざっくりとメモリの山を確保してしまいます。この段階では、まだアプリはリアルタイム動作していません。
そして、諸々の初期化が終わった後でリアルタイムアプリがいよいよリアルタイム動作を開始するとします。そして、ある時メモリが欲しくなりました。
ここでリアルタイムアプリがLinuxカーネルにメモリを要求すると、リアルタイム性が失われてしまいます。そこで、「ざっくりメモリ山」からメモリを取り出して使う事にします。
「ざっくりメモリ山」自体を確保したり、そこからメモリを1部とりだしたりするのは、RTAIがやってくれます。ですから、アプリ側からは、「ざっくり山を確保せよ!」「ざっくり山から、これだけのメモリをよこせ!」とRTAIのAPI経由で命令するだけでOKです。
このメモリ山へのアクセスは、複数のタスク、あるいは複数のプロセスから可能です。ユーザーモード、カーネールモードの壁を越えてメモリを共有する事も可能です。
図2 ざっくり山には「グローバルヒープ」と「グループヒープ」がある
さて、このざっくりメモリ山ですが、RTAIには2種類あります。1つはグローバルヒープ、もう1つはグループヒープです。
グローバルヒープというのは、RTAIのカーネル構築時にサイズを指定しているもので、ずっと存在するヒープです。
グループヒープというのは、RTAIに依頼して動的に確保する「ざっくり山」です。
これらにアクセスするには、それぞれ別々のAPIを使用します。ただし、使い方そのものに大きな違いはありません。
FIFO
FIFOは、以前の記事でご紹介したメールボックスとおなじような振る舞いをします。特別な事情が無いのであれば、まよわずメールボックスを使用するのがお勧めです。
あえてFIFOを使う理由があるとすれば、それはFIFOがリアルタイムタスク以外からでも操作できる、という点にあります。
メールボックスは、メールボックスへの送信・受信ともにリアルタイムタスクからしか行えませんでした。しかし、FIFOを使用すればリアルタイムタスク以外からのデータ送受信が可能です。
これを使うケースとしては、たとえば割り込みルーチンなどがあります。割り込みルーチンはリアルタイムタスクではありません。(割り込みに応じて即動作する、という意味ではリアルタイムです。しかし、ここでいうリアルタイムタスクとは、「RTAIのAPIを使用して、スレッドをリアルタイム化したもの」の事を言っています)
この割り込みルーチンのように、非リアルタイムなタスクから、リアルタイムタスクとデータをやりとりしたい場合には、FIFOは有用な手段です。
さて、このFIFOは特殊デバイスファイルの形式をとります。まず、次のようにして初期化を行います。
mknod /dev/rtf<x> c 150 <x>
ここで、<x>は0~63の数値です。つまり、64チャンネルのFIFOが使用できる事になります。
ここで作ったFIFOは、ユーザーモードからはファイルと同じようにアクセスできます。一方、カーネルモードではRTAIのAPIを使用してアクセスします。
セマフォ
いわゆるセマフォというやつです。FIFOと同様に、カーネルモードでもユーザーモードでも使用可能です。さらに、FIFOと同様にリアルタイムタスク以外からも使用可能です。
セマフォへのアクセスは、FIFOと同様に /dev/rtf<x> を使用します。つまり、64個のセマフォが使用可能です。
ちなみに、セマフォというのはカンバンのようなものです。
図3 セマフォに対しては、post と wait を行う
セマフォに対する操作は、post と wait です。
post とは、セマフォに対してカンバンを1つ送ります。
wait とは、セマフォからカンバンを1つ取り出します。図3では、カンバンが2つ入っていますので、2つまでは即取り出し可能です。
もしも、セマフォにカンバンが1つも入っていない状態で wait すると、waitをしたタスクは一時停止します。そして、だれかが新たにセマフォにカンバンを入れてくれた時点でふたたびタスクが動き始めます。
このように、必要な時に必要なだけタスクを動かす、という時にセマフォを使用します。
たとえば、図3の左側のタスクが「データ測定」タスクだとしましょう。右側のタスクが「データ処理」タスクです。データ処理をするためには、ある程度データ測定しておかないといけません。そこでセマフォの登場です。
データの準備が出来るまでは、データ処理タスクをセマフォでもって眠らせておきます。そして、データ測定タスクがある程度データを貯めたら、セマフォに対して post します。
セマフォに post されたら、今まで眠っていたデータ処理タスクが起きだして、データの処理を行います。
このように、「生産者」と「消費者」が協調して動作させたい場合、セマフォが有用です。
次回
次回は、IPC編その4として、Net_RPCについて書きます
有用な記事をありがとうございます。
今さらながらになりますが、共有Memoryについて使用してみた結果、一つのグループヒープには上限があり、少なくとも32bit環境では64KBのようです(64bitは未確認です)。
たいていは問題ないと思いますが、Sysytemによっては不足となり、工夫が必要になります。
もし拡張する方法があれば、構成が楽になるのですが…。
上記のコメントの後日談、追加です。
Qtを使ってデバッグしていたときには64KBの壁がありましたが、QtではなくTerminalからプログラムを起動したときには64KB以上のサイズでもエラーになりませんでした。
rtai4.0で確認しました。
最初の頃はQtからのみ起動しており、64KB以下でないとエラーとなったため、すっかりそのように思い込んでいました。
久しぶりに訪問しました。ついでに追加情報を一つ。
最近RTAI5.2とdebian9.11の64bit環境で確認しましたが、Qtを使用すると64KBの壁が立ちはだかります。
なかなか変わらないものですね。
Qtに連絡すれば改善されるかもしれませんが、ま、いいかと気力不足です(笑)