第 3 章 初歩の RTLinux プログラミング 27
3.7 LinuxプロセスからRTLinuxモジュールを制御するプログラム
3.7.4 Linuxプロセスの動き
50〜55行の処理は、周期実行している時の、処理コードです。
内容は、内部counter変数をインクリメントし、counter変数の値を、rtf_put関数を使って、RT-FIFO に書き込み続けています。
ここでやっているのは、非常に単純な処理ですが、この処理をDIOやAD/DA等に置き換えて見る と、様々なやり方が想定できることでしょう。
例えば、周期実行の部分に、1件のAD入力関数を配置するだけで、サンプリング間隔を簡単に可 変することが実現できます。
■関数解説
★rtf_create
RT-FIFOを生成します。
第1引数でRT-FIFOのID値を、第2引数でサイズを指定します。
生成したRT-FIFOを破棄するには、rtf_destroy関数を用います。
★rtf_create_handler
この関数は、ユーザ空間からRT-FIFOにデータが書き込まれた際に、呼び出される関数を登録します。
第1引数には、RT-FIFO作成時に指定したID値を指定します。
第2引数には、RT-FIFOにデータが書き込まれた際に、呼び出される関数を指定します。
この関数は、ユーザ空間からRT-FIFOにデータが書き込まれた際に呼ばれる関数を登録しますが、カー ネル空間からアクセスされた(rtf_put、rtf_get関数を使用した)場合に呼ばれる関数を登録するには、
rtf_create_rt_handler関数を使用します。
★pthread_wakeup_np
この関数を呼ぶことで、指定したRTLinuxスレッドの実行を再開することができます。
★pthread_suspend_np
引数により指定したRTLinuxスレッドをpthread_wakeup_npが呼び出されるまで中断します。
(25〜33行目:RT-FIFOのオープン)
25〜33行のopen関数は、RTLinuxモジュールのinit_module関数内のrtf_create関数で作成した
RT-FIFOをオープンします。
Linuxプロセスでは、RT-FIFOは、open, read, write, closeの低レベル入出力関数にてアクセス可能で
す。
rtf_create関数の第1引数で指定するID値と、open関数で用いるデバイス名は、相対的な関係があり
ます。
両者の関係を、以下に示します。
Linuxプロセス側 RTLinuxモジュール側
“/dev/rtf1” GET_HANDLER_FIFO = 1
“/dev/rtf2” GET_TASK_FIFO = 2
“/dev/rtf3” SET_TASK_FIFO = 3
RTLinuxプログラミングでのRT-FIFOは、”/dev/rtfXX”というデバイス名にてアクセス可能です。
そして、デバイス名の数値の部分は、rtf_create関数のID値が、そのまま割り当てられます。
(36〜41行目:RTLinuxモジュールの周期実行開始)
38行目のwrite関数は、RTLinuxモジュールに対してRtlinuxスレッドの周期実行スタートを指示し ています。
CMD_STRUCT構造体のidメンバ変数にID_START、valueメンバ変数に500(実行周期を500msに指 定している)を指定しています。
ここで与えられたパラメータは、RTLinuxモジュールのハンドラ(my_handler)を経由し、RTLinux スレッド(my_task)に、コマンドとして伝達されます。
(44〜57行目:RTLinuxモジュールからのカウンタ値取得)
44〜57行の処理は、RTLinuxモジュール内のRTLinuxスレッドからカウンタ値を順次読み取ってい
き、読み取った値が10を超えた時点で、whileループを抜けています。
もう一度、『48ページ 3.7.3 RTLinuxモジュールの動き』のRTLinuxスレッドの処理を見返して みてください。
51〜56行で、内部counter変数をインクリメントして、RT-FIFOに値を書き込んでいると思います。
ここで書き込んだ値が、この処理の中で読み取られているのです。
次に、select関数と一連のマクロの動きを説明します。
この処理は、まずselect関数で、RT-FIFOにデータが入ってくるのを待機し、データを受け取ると、
FD_ISSETマクロで、確かに自分のRT-FIFOにデータが入っていることを確認した上で、データを 読み取っています。
select関数
RT-FIFOのハンドル(fd_ret)に対 し、データ待ちを行う。
fd_retデータ有り? 読み取り
RT-FIFO(fd_ret)からデータを取 り込む
有る
無い FD_ISSETで検出
リストでは、1つのRT-FIFOからしか値を読み取ってないので、わざわざselect関数を用いる必要は ありませんが、複数のRT-FIFOを同時に読み取ろうとした時、このselect関数を使用することで、
データのあるRT-FIFOからのみデータを読み取ることが可能となります。
select関数
複数のRT-FIFOのハンドルに対 し、データ待ちを行う。
データ有り? 読み取り1
RT-FIFOからデータを取り込む
有る
無い FD_ISSETで検出
データ有り?
有る
無い
読み取り2
RT-FIFOからデータを取り込む
FD_ISSETで検出
(60〜65行目:RTLinuxモジュールの周期実行停止)
62行のwrite関数は、RTLinuxモジュールに対してRTLinuxスレッドの周期実行ストップを指示して
います。
先の実行開始の時は、idメンバ変数にID_STARTをセットしていましたが、ここではID_STOPをセ ットしています。ここで与えられたパラメータは、RTLinuxモジュールのハンドラを経由して RTLinuxスレッドに対して、ストップ命令として伝えられます。
以上で、基本的なRTLinuxプログラミングは終わりです。
■関数&用語解説
★Linuxプロセスが終了しても、RTLinuxモジュールは自動で取り外されません。
Linuxプロセスの実行が終了しても、RTLinuxモジュールは自動で取り外されることはありません。
Linuxプロセスの処理を終了させた後、lsmodコマンドを打ち込んでみてください。RTLinuxモジュール
が常駐していることに気付くことでしょう。
# lsmod
Module Size Used by
module3 1472 0 (unused) ← RTLinuxモジュールが常駐している rtl_sched 43104 0 [module3]
rtl_fifo 9968 0 [module3]
rtl_posixio 7184 0 [rtl_fifo]
rtl_time 10000 0 [module3 rtl_sched rtl_posixio]
rtl 27184 0 [module3 rtl_sched rtl_fifo rtl_posixio rtl_time]
★Linuxプロセスは、複数起動が可能か?
先程のプログラムを実行していて、疑問に思いませんでしたか。
「sample3を複数同時に動かすことは、できるのだろうか?」
実際に何度かテストした結果を示します。
ケース1:「fail open /dev/rtf1」と表示されて終了。
ケース2:「get counter:11」と表示されて終了
ケース1は、あるLinuxプロセスがRT-FIFOをオープン中かつ使用中なので、オープンできなかったこ
とを意味します。
これは、このサンプルでは、RT-FIFOがオープンできるプロセスは、たかだか1つだけであり、共有オー プンはできないことを意味します。
ケース2は、やや複雑です。これは、以前のプロセスが、RT-FIFOに格納されたバッファデータがクリ アされない内にオープンし、意図しないデータを読んで終了してしまったことを意味します。
このことから、LinuxプロセスとRTLinuxモジュールを繋ぐRT-FIFOを複数に増やせば、複数のLinux プロセスから制御できそうだ、と予想できます。
★open
第1引数には、オープンするデバイスのパスを指定し、第2引数には、読み取り用のフラグO_RDONLY もしくは、書き込み用のフラグO_WRONLYを指定しています。
★ディスクリプタ
open関数の実行が成功すると、戻り値としてファイルディスクリプタが返されますが、この値は、現在 使用されていないファイルディスクリプタのうち、負でない最小の値が返されます。
★FD_ZERO、FD_SETマクロ
ここで使用しているFD_ZEROマクロとFD_SETマクロは、select関数に関係のあるものです。後方に記 述しているselect関数は、複数のディスクリプタに変化があるまで待つ関数です。ここでは、RT-FIFO しか監視する必要はないため、複数のファイルディスクリプタを監視する必要はありません。そのため、
最初にFD_ZEROマクロにより指定したファイルディスクリプタの集合(ここではrfds)をクリア(消去)し、
次に、先程クリアしたファイルディスクリプタの集合(rfds)にRT-FIFOのディスクリプタ(fd_ret)を設定す ることにより、select関数は、RT-FIFOの変化のみを監視するようになります。
★タイムアウト
select関数では、指定したファイルディスクリプタの変化を監視する時間を設定することができます。
select関数の第5引数として、タイムアウト時間に0を指定するとすぐに処理が抜け、NULLを指定する
と、指定したファイルディスクリプタに変化があるまでずっと待ちます。
★select
select関数の第1引数には、監視するファイルディスクリプタの個数を指定します。例えば5を指定する
と、0〜4の5つのファイルディスクリプタを監視します。ここでは、使用できるファイルディスクリプ タの最大値であるFD_SETSIZEを指定しています。
第2引数には、読み込み可能かどうかを監視するファイルディスクリプタの集合、第3引数には書き込 み可能かどうかを監視するファイルディスクリプタの集合、第4引数には例外の監視を行うファイルデ ィスクリプタの集合を指定します。ここでは、RT-FIFOからデータを読み出すので、先程FD_ZEROマ クロ、FD_SETマクロで設定したファイルディスクリプタの集合を第2引数に設定します。
第5引数には、select関数のタイムアウト時間を設定するtimeval構造体を指定します。ここをNULLに 指定すると、タイムアウトが発生しなくなりますので、ご注意ください。
★FD_ISSET
FD_ISSETマクロは、第2引数で指定されるファイルディスクリプタの集合に、第1引数で指定される
ファイルディスクリプタが存在するかをチェックします。FD_ISSETが真を返せば、集合の中に指定し たファイルディスクリプタが存在することになります。
★read
read関数の第1引数には読み込みを行うファイルディスクリプタ、第2引数には読み込みデータを格納 するバッファ、第3 引数には読み込みを行うサイズを指定します。戻り値には、実際に読み込み取得で きたバイト数が返されます。
★コンパイル時の注意事項
RTLinuxのプログラムを作成する際には、Makefile内で「rtl.mk」をincludeすることで、コンパイルに必
要なフラグや識別子が全てセットされます。
ここで注意が必要なのは、このフラグにコードの最適化を行う-02 や、デバッグ情報を添付する-g 等が デフォルトで指定されていることです。
rtl.mk をそのまま使用する場合には、使用されているフラグを確認してください。そうしないと、バグ
を埋め込む原因になるかもしれません。
(1) 修正前
while(*memadr & CMPL);
(2) 修正後 while(1){
BYTE Status;
memcopy(&Status, memadr, 1);
if(Status & CMPL) break;
}
上記(1)のコードでは、アドレス memadrの値を参照して、データが書き込まれた場合にループを抜ける 処理となっています。
このコードを最適化フラグを有効にしてコンパイルすると、環境によってはメモリの参照を一度しか行 わない場合があります。そうなってしまうと、最初に参照した時点でメモリの値が0であった場合に、
無限ループに入ってしまいます。
最適化によるコンパイラの動作は環境にも依存します。一概には言えませんが、デフォルトで設定され ているコンパイラのフラグを念頭に置いておくと、デバッグ時に無用な苦労を減らせるかもしれません。