  RTLinux HOWTO
  Dinil Divakaran <mailto:dinildivakaran@rediffmail.com>
  1.1, 2002-08-29
  日本語訳: yomoyomo <ymgrtq@ma.neweb.ne.jp>
  v1.1j 2003 年 01 月 18 日

  RTLinux の導入と Linux でリアルタイムなプログラムを書くことについて
  ______________________________________________________________________

  目次

  1. はじめに
     1.1 目的
     1.2 この HOWTO を読むべき人
     1.3 謝辞
     1.4 フィードバック
     1.5 配布の方針

  2. RTLINUX のインストール
  3. なぜ RTLinux なのか
  4. RTLinux のプログラムを書く
     4.1 モジュールの書き方入門
     4.2 RTLinux におけるスレッドの作成
     4.3 例プログラム

  5. コンパイルと実行
  6. プロセス間通信
     6.1 リアルタイム FIFO
     6.2 FIFO を利用するアプリケーション

  7. 次に行うこと
  8. 日本語訳について

  ______________________________________________________________________

  1.  はじめに

  1.1.  目的

  この文書は、初心者のユーザを助け、できるだけ簡単に RTLinux を稼働でき
  るようにすることを目的とします。

  1.2.  この HOWTO を読むべき人

  この文書は、リアルタイムカーネルの動作を知りたいと思っている人達すべて
  を対象にしています。既にモジュールプログラミングに精通している人達は、
  この文書を難しいものとは感じないでしょう。そうでない人達にしても、心配
  する必要はありません。モジュールプログラミングの基本概念しか必要ありま
  せんし、それについては必要なところで実際に議論しますので。

  1.3.  謝辞

  まず何より、私を励まし助けてくれた、私の相談相手である Pramode C. E に
  感謝したいと思います。また、Victor Yodaiken にも心からの感謝を述べさせ
  てもらいます。この文書は、Victor Yodaiken が寄稿した種々の論文から取り
  入れた情報なしには成立しなかったでしょう。また、"A Linux-based Real-
  Time Operating System" という論文を書いた Michael Barabanov にも感謝し
  ます。

  1.4.  フィードバック

  この文書に関する疑問や意見はどんなものでも常に歓迎します。遠慮なく私に
  <mailto:dinildivakaran@rediffmail.com> メールしてください。この文書中
  に何か間違いを見つけたら、次版で修正できるように私に知らせてください。
  ご協力感謝します。

  1.5.  配布の方針

  Copyright (C)2002 Dinil Divakaran.

  この文書はフリーです。Free Software Foundation により発行されている
  GNU General Public License バージョン2、もしくは(任意で) それ以降の
  バージョンの条件に従ってこの文書を再配布したり修正できます。

  この文書は役に立つことを期待して配布されるものですが、いかなる保証も行
  いません。商品性や特定用途への適合性についての暗黙の保証すらありませ
  ん。詳しくは、GNU General Public License をご覧ください。

  2.  RTLINUX のインストール

  RTLinux カーネルのコンパイルの第一段階は、予めパッチのあたったカーネル
  2.2.18
  <http://ftp.kernel.org/pub/linux/kernel/v2.2/linux-2.2.18.tar.gz> (x86
  only) か 2.4. 0-test1
  <http://ftp.kernel.org/pub/linux/kernel/v2.4/linux-2.4.0-test1.tar.gz>
  (x86, PowerPC, Alpha) をダウンロードして /usr/src/ で展開することで
  す。また www.rtlinux.org <http://www.rtlinux.org> から RTLinux カーネ
  ル(バージョン3.0)を新たにコピーして /usr/src/rtlinux/ に置いてくださ
  い。(この文書では、プロンプトを表すのに $ を使います)

  1. それでは Linux カーネルを設定してください:

                     $ cd /usr/src/linux
                     $ make config
                             か
                     $ make menuconfig
                             もしくは
                     $ make xconfig

  2. カーネルイメージをビルドするには、以下のように入力してください:

                     $ make dep
                     $ make bzImage
                     $ make modules
                     $ make modules_install
                     $ cp arch/i386/boot/bzImage /boot/rtzImage

  3. 次に LILO の設定を行います。/etc/lilo.conf ファイルに以下の4行を追
     加してください。

                     image=/boot/rtzImage
                     label=rtl
                     read-only
                     root=/dev/hda1

  警告: 上記の /dev/hda1 を、あなたの環境におけるルートファイルシステム
  に置き換えてください。しかるべきファイルシステムを見つける最も簡単な方
  法は、 /etc/lilo.conf ファイルの既存のエントリにおける "root=" を見る
  ことです。

  4. それではコンピュータを再起動し、LILO プロンプトで 'rtl' と入力して
     RTLinux カーネルをロードしてください。それから /usr/src/rtlinux/ に
     'cd' し、RTLinux を設定します。

                     $ make config
                             か
                     $ make menuconfig
                             もしくは
                     $ make xconfig

  5. ようやく RTLinux のコンパイルです

                     $ make
                     $ make devices
                     $ make install

  最後の手順により、以下のディレクトリが作成されます:

  /usr/rtlinux-xx (xx はバージョンを意味する)

  ここは RTLinux のデフォルトのインストール先となるディレクトリで、ユー
  ザプログラムを作成したりコンパイルするのに必要なもの (include ファイ
  ル、ユーティリティ、ドキュメントなど) が入ります。また、最後の手順によ
  り /usr/rtlinux-xx を指す以下のシンボリックリンクが作成されます。

  /usr/rtlinux

  将来にわたる互換性を確保するために、あなた自身が作成したすべての
  RTLinux 用プログラムにおいて、デフォルトのパスに /usr/rtlinux を適用す
  るようにしてください。

  注記: 何か Linux カーネルのオプションを変更する場合は、以下の手順を行
  うことをお忘れなく:

                  $ cd /usr/src/rtlinux
                  $ make clean
                  $ make
                  $ make install

  3.  なぜ RTLinux なのか

  標準の Linux カーネルの動作を調べれば、RTLinux を設計した理由が分かり
  ます。 Linux カーネルはハードウェアをユーザレベルのタスクから切り離し
  ます。カーネルはスケジューリングアルゴリズムを用い、平均的に優れた性
  能、スループットを提供するよう各タスクに優先度を割り当てます。従って、
  どのユーザレベルのタスクであっても、そのタスクが CPU により割り当てら
  れているタイムスライスを超過したら、 Linux カーネルはタスクを停止でき
  ます。このスケジューリングアルゴリズムに加え、デバイスドライバ、割り込
  み不可能なシステムコール、割り込み無効化や仮想メモリ操作などが予測不可
  能性(unpredictability) の原因になります。つまり、以上の要素がタスクの
  リアルタイム性能の障害になっています。
  Linux がリアルタイムな性能を保証しないことは、例えば 'mpg123' などのプ
  レイヤーで音楽を聴いているなら、既にご承知のことかもしれません。予め決
  められたタイムスライスでプロセスを実行した後で、標準の Linux カーネル
  はタスクを中断して CPU に別のタスク(例えば、X サーバや Netscape の立ち
  上げを行うタスク)を割り当てることが可能です。結果として、音楽の連続性
  が失われてしまいます。このように、 CPU 時間をすべてのプロセスに公平に
  分配しようと試みることにより、カーネルは他のイベントが起こらないように
  できるのです。

  リアルタイムカーネルならば、そのカーネルの元で動くプロセスが要求するタ
  イミングを保証することが可能なはずです。RTLinux カーネルは、上で議論し
  た予測不可能性の原因となるものを除去することによりリアルタイム性能を実
  現しています。RTLinux カーネルは、標準の Linux カーネルとハードウェア
  の間に位置すると考えることができます。 Linux カーネルは、リアルタイム
  層を事実上のハードウェアとみなします。つまりユーザは、一つ一つのタスク
  の導入とその優先度の設定の両方が行えます。ユーザはスケジューリングアル
  ゴリズム、優先度、実行頻度などを決めることでプロセスの正確な実行タイミ
  ングを実現できます。 RTLinux カーネルは、標準の Linux カーネルに最低の
  優先度を割り当てます。こうすることでユーザタスクがリアルタイムに実行さ
  れることになります。

  実際のリアルタイム性能は、すべてのハードウェア割り込みを遮断することに
  より達成されます。RTLinux に関連する割り込みに対してのみ、適切な割り込
  みサービスルーチンが動作します。それ以外の割り込みは全て一旦保留さ
  れ、RTLinux カーネルがアイドル状態になり標準の Linux カーネルが稼働し
  たときにソフトウェア割り込みとしてカーネルにわたります。 RTLinux の実
  行部そのものはノンプリエンプティブです。

  リアルタイムタスクは優先され(つまりそれらはハードウェアに直接アクセス
  する)、仮想メモリは利用しません。リアルタイムタスクは、メモリに動的に
  ロード可能な、特殊な Linux モジュールとして書かれます。リアルタイムタ
  スクの初期化コードは、リアルタイムタスク構造体を初期化し、 RTLinux
  カーネルにその期限、周期、そして解放時間制限を通知します。

  Linux カーネルには手を加えないので、RTLinux は Linux カーネルと共存し
  ます。比較的単純な修正を一通り行えば、将来の Linux 開発の妨げになるこ
  となく、既存の Linux カーネルをハードリアルタイム環境にうまく変えるこ
  とができます。

  4.  RTLinux のプログラムを書く

  4.1.  モジュールの書き方入門

  さてモジュールとは何でしょうか? Linux のモジュールは、通常 gcc に -c
  フラグ引数を付けて生成されたオブジェクトファイルに過ぎません。モジュー
  ル自体は、main() 関数のない普通の C 言語ファイルをコンパイルすることに
  より生成されます。main() 関数がない代わりに、init_module 関数と
  cleanup_module 関数のペアが含まれます:

  o  init_module() は、そのモジュールをカーネルに組みこむ際にコールされ
     ます。成功時には 0 を返し、失敗したときには負の値を返します。

  o  cleanup_module() は、そのモジュールを削除する直前にコールされます。

  通常 init_module() では、カーネルに何か処理を行うハンドラを登録する
  か、カーネルの関数の一つを独自のコード(通常は何か行い、それから元の関
  数をコールするコード)に置き換えるかします。 cleanup_module() 関数
  は、init_module() が行ったことすべてを元に戻すことになっているので、モ
  ジュールを安全に外すことが可能です。

  例えば、module.c という名前の C 言語ファイル(main() 関数の代わりに
  init_module() と cleanup_module() がある)を書いたとしたら、以下のよう
  に入力すれば、そのコードがモジュールに変換されます:

          $ gcc -c {SOME-FLAGS} my_module.c

  このコマンドにより module.o という名前のモジュールファイルが生成されま
  す。 module.o は 'insmod' コマンドを使用してカーネルに組みこめます:

          $ insmod module.o

  同様にモジュールを削除するには 'rmmod' を使えばよいです:

          $ rmmod module

  4.2.  RTLinux におけるスレッドの作成

  通常リアルタイムアプリケーションは、複数の実行「スレッド」で構成されま
  す。スレッドは、共通のアドレス空間を共有する軽量のプロセスで
  す。RTLinux では、すべてのスレッドが Linux カーネルのアドレス空間を共
  有します。スレッドを使うことの利点は、コンテキストスイッチに比べてス
  レッド間の切り替えが極めて軽いことにあります。以下の例で紹介する各種関
  数を利用すれば、スレッドの実行を完全に制御できます。

  4.3.  例プログラム

  スレッドの動作を理解するのに最も良いのは、リアルタイムプログラムをト
  レースすることです。例えば、以下に示すプログラムは、1秒に一度実行し、
  その度に 'Hello World' と出力します。

  プログラムコード (file - hello.c) :

  #include <rtl.h>
  #include <time.h>
  #include <pthread.h>

  pthread_t thread;

  void * thread_code(void)
  {
          pthread_make_periodic_np(pthread_self(), gethrtime(), 1000000000);

          while (1)
          {
                  pthread_wait_np ();
                  rtl_printf("Hello World\n");
          }

          return 0;
  }

  int init_module(void)
  {
     return pthread_create(&thread, NULL, thread_code, NULL);
  }

  void cleanup_module(void)
  {
     pthread_delete_np(thread);
  }

  それでは init_module() から始めましょう。init_module() が
  pthread_create() をコールしています。pthread_create() は、コールするス
  レッドと同時に動作する、新規のスレッドを作成する関数です。この関数
  は、Linux カーネルスレッドからしか (つまり、init_module() を用いてし
  か)コールしてはいけません。

          int  pthread_create(pthread_t  * thread,
                              pthread_attr_t * attr,
                              void * (*thread_code)(void *),
                              void * arg);

  作成された新規スレッドの型は pthread_t で、これは pthread.h ヘッダファ
  イルで定義されています。このスレッドは、関数 thread_code() を実行し、
  それに arg を引数として渡します。引数 attr は、新規スレッドに適用され
  るスレッド属性を指定します。もし attr が NULL なら、デフォルトの属性が
  適用されます。

  ここでは thread_code() は引数なしでコールされます。thread_code は、初
  期化、ランタイム、そして終了という三つの構成要素から成ります。

  初期化フェーズでは、pthread_make_periodic_np() をコールします。

          int pthread_make_periodic_np(pthread_t thread,
                                       hrtime_t start_time,
                                       hrtime_t period);

  pthread_make_periodic_np は、実行の準備ができたスレッド thread を指定
  します。スレッドは start_time で指定された時間に実行を開始し、ナノ秒単
  位で指定された period の間動作します。
  gethrtime は、システムがブートしてからの時間をナノ秒単位で返します。

         hrtime_t gethrtime(void);

  この時間はリセットされることも修正されることもありません。gethrtime は
  常に、単調に増加する値を返します。hrtime_t は64ビットの符号付き整数型
  です。

  pthread_make_periodic_np() 関数をコールすると、スレッドは自分を 1Hz の
  頻度で定期的に実行するよう、スケジューラに命令します。これでスレッドの
  初期化部は終わります。

  while() ループは pthread_wait_np() 関数のコールで始まります。この関数
  は、次の周期が開始するまで、現在動作しているリアルタイムスレッドの実行
  を停止します。このスレッドは前に pthread_make_periodic_np により実行指
  定がされています。スレッドが再びコールされると、 pthread_wait_np() が
  あらためてコールされるまで while ループ内の残りの処理を実行します。

  ループを抜ける手段を入れていないので、このスレッドは 1Hz 間隔で永久に
  実行され続けることになります。プログラムを停止する唯一の手段は、 rmmod
  コマンドによりそれをカーネルから削除することです。 rmmod は
  cleanup_module() をコールしますが、これが スレッドを中止し、そのリソー
  スを解放する pthread_delete_np() をコールするのです。

  5.  コンパイルと実行

  hello.c プログラムを実行するには(もちろんですが、rtlinux のブート後に
  です)、以下の手順を行わなければなりません:

  1. GCC コンパイラを使ってソースコードをコンパイルし、モジュールを生成
     します。とはいえ、Makefile を作成して作業を簡略化するほうが良いで
     す。そうすればソースコードをコンパイルするのに 'make' と入力するだ
     けで済みます。Makefile は、'Makefile' という名前のファイルに以下の
     内容を入力することで作成できます。

          include rtl.mk
          all: hello.o
          clean:
              rm -f *.o
          hello.o: hello.c
              $(CC) ${INCLUDE} ${CFLAGS} -c hello.c

  2. rtl.mk ファイルを見つけ、hello.c や Makefile があるのと同じディレク
     トリに置いてください。rtl.mk ファイルは、コードをコンパイルするのに
     必要なすべてのフラグを含む include ファイルです。rtl.mk ファイルは
     RTLinux のソースツリーからコピーして hello.c ファイルと同じディレク
     トリに格納できます。

  3. コードをコンパイルするには、'make' コマンドを使用してください。

             $ make

  4. この結果できるオブジェクトバイナリは、カーネルの RTLinux により実行
     される部分に組みこまなくてはなりません。'rtlinux' コマンドを使用し
     てください (それには 'root' になる必要があります)。
             $ rtlinux start hello

  これで hello.o プログラムがメッセージを毎秒出力するのを確認できるはず
  です。マシンの設定によっては、コンソールで直接見ることができるはずです
  が、そうでなければ以下のコマンドを入力すれば見ることができます:

          $ dmesg

  プログラムを停止するには、これをカーネルから削除する必要があります。そ
  れを行うには、以下のコマンドを入力してください:

          $ rtlinux stop hello

  モジュールの組みこみ、削除を行う別のやり方として、それぞれ insmod と
  rmmod を利用するというのもあります。

  ここまでは例となるプログラムが簡単過ぎました。これまで見てきたものと違
  い、実際には一つのプログラム中に複数のスレッドが存在するかもしれませ
  ん。優先度をスレッド作成時に設定したり、それを後で修正することが可能で
  す。また、しかるべきスケジューリングアルゴリズムを選択することも可能で
  す。実は、独自のスケジューリングアルゴリズムを書くことも可能なんです!

  我々の例では、thread_code() 関数の先頭に以下の3行を挿入することで、ス
  レッドの優先度を1に設定し、FIFO スケジューリングを選択できます。

          struct sched_param p;
          p . sched_priority = 1;
          pthread_setschedparam (pthread_self(), SCHED_FIFO, &p);

  6.  プロセス間通信

  これまで見てきた例プログラムは、リアルタイムプロセスと呼ばれるもので
  す。アプリケーションプログラムのすべての部分をリアルタイム用に書く必要
  はありません。プログラムの中でも正確な時間制限を要求される部分だけがリ
  アルタイムプロセスとして書かれるべきであることが分かります。それ以外は
  ユーザ空間で実行するように書いてかまいません。ユーザ空間のプロセスは、
  多くの場合リアルタイムスレッドよりも作成、実行、そしてデバッグが容易で
  す。しかしその一方で、ユーザ空間にある Linux プロセスとリアルタイムス
  レッドの間で通信を行う手段を用意すべきです。

  プロセス間通信を行う手段はいくつかあります。通信を行う最も有力で一般的
  な手段であるリアルタイム FIFO について議論します。

  6.1.  リアルタイム FIFO

  リアルタイム FIFO は、一方向のキューです(First In First Out:先入れ先出
  し)。つまり一方からプロセスが FIFO にデータを書くと、FIFO のもう一方か
  ら情報が別のプロセスに読みこまれます。通常、そうしたプロセスの片側がリ
  アルタイムスレッドで、もう一方がユーザ空間のプロセスになります。

  リアルタイム FIFO は、実際にはメジャーナンバー150のキャラクターデバイ
  ス (/dev/rtf*)です。リアルタイムスレッドは、各 FIFO を参照するのに整数
  を用います(例えば /dev/rtf2 なら2)。 FIFO の数には制限があります。FIFO
  を扱う関数には、rtf_create(), rtf_destroy(), rtf_get(), rtf_put() など
  があります。

  一方、Linux のユーザプロセスはリアルタイム FIFO を普通のキャラクターデ
  バイスとみなします。そのため、open(), close(), read() そして write()
  といった関数がこれらのデバイスに対して利用できます。

  6.2.  FIFO を利用するアプリケーション

  まず PC のスピーカーから(二つの音だけからなる)音楽を演奏する簡単な C
  プログラム(ファイル名は pcaudio.c)を考えてみましょう。当面は、音符を鳴
  らすにはキャラクターデバイス /dev/rtf3 に書きこむだけでよいことにしま
  しょう(後で、この FIFO (/dev/rtf3)から読みこみ、 PC のスピーカーに送信
  するリアルタイム時間プロセスを考えます)。

  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <unistd.h>

  #define DELAY 30000

  void make_tone1(int fd)
  {
          static char buf = 0;
          write (fd, &buf, 1);
  }

  void make_tone2(int fd)
  {
          static char buf = 0xff;
          write (fd, &buf, 1);
  }

  main()
  {
          int i, fd = open ("/dev/rtf3", O_WRONLY);
          while (1)
          {
                  for (i=0;i<DELAY;i++);
                  make_tone1(fd);
                  for (i=0;i<DELAY;i++);
                  make_tone2(fd);
          }
  }

  さて、上に示したプログラム(pcaudio.c)をコンパイルして走らせると、方形
  波に相当する規則的な音を生成するはずです。しかしその前に '/dev/rtf3'
  から読みこみを行い、対応するデータを PC のスピーカーに送信するモジュー
  ルが必要です。このリアルタイムプログラムは、 rtlinux のソースツリー
  (/usr/src/rtlinux/examples/sound/)で見つけられます。'insmod' コマンド
  を用いて sound.o モジュールを組みこんでください。

  デバイスから読みこみを行うモジュールを組みこんだので、プログラムを実行
  できるようになりました('gcc' でコンパイルし、それによってできた
  'a.out' を実行してください)。システム内に他の(時間を消費する)プロセス
  がない場合は、プロセスはある程度規則的な音を生成します。しかし、別のコ
  ンソールで X サーバが起動されていると、無音部が長くなります。また
  'find' コマンドを(/usr ディレクトリにあるファイルに対して)実行したりす
  ると、音像はすっかりゆがんでしまいます。こうなるのは、我々が非リアルタ
  イム的に FIFO 上にデータを書きこんでいるからです。

  それでは、音が何の障害もなく生成されるように、リアルタイムにこのプロセ
  スを走らせる方法を考えます。まず上記のプログラムをリアルタイムなプログ
  ラムに変換します(ファイル名は rtaudio.c)。

  #include <rtl.h>
  #include <pthread.h>
  #include <rtl_fifo.h>
  #include <time.h>

  #define FIFO_NO 3
  #define DELAY 30000
  pthread_t thread;

  void * sound_thread(int fd)
  {
          int i;
          static char buf = 0;
          while (1)
          {
                  for(i=0; i<DELAY; i++);
                  buf = 0xff;
                  rtf_put(FIFO_NO, &buf, 1);

                  for(i=0;i<DELAY;i++);
                  buf = 0x0;
                  rtf_put(FIFO_NO, &buf, 1);
          }
          return 0;
  }

  int init_module(void)
  {
          return pthread_create(&thread, NULL, sound_thread, NULL);
  }

  void cleanup_module(void)
  {
          pthread_delete_np(thread);
  }

  まだ行ってないなければ、sound.o モジュールをカーネルの中に「プラグイ
  ン」してください。(前に解説した通りに)それ用の Makefile を書き、上に示
  したプログラムをコンパイルして 'rtaudio.o' モジュールを作成してくださ
  い。このモジュールを組みこむ前に、もう一つ行っておくことがあります。上
  記のプログラムが無限ループの中を回ることに注意してください。つまり、ス
  レッドのスリープか停止を行うコードを入れないと、そのスレッドは実行を止
  めません。簡単に言えば、PC のスピーカーは音を鳴らし続け、何か他のこと
  を行うにはコンピュータを再起動しなくてはならなくなります。

  そこで、スレッド自身が音の間に遅延を入れるように、(sound_thread() 関数
  の)コードを少しだけ変えてみましょう。
  void * sound_thread(int fd)
  {
          static char buf = 0;
          pthread_make_periodic_np (pthread_self(), gethrtime(), 500000000);

          while (1)
          {
                  pthread_wait_np();
                  buf = (int)buf^0xff;
                  rtf_put(FIFO_NO, &buf, 1);
          }
          return 0;

  }

  今度は、'rmmod' コマンドを用いてモジュールを削除するだけでプロセスを停
  止させられます。

  ここまでリアルタイム FIFO がプロセス間通信でどのように利用できるかを見
  てきました。また RTLinux が本当に必要であることが、上記の例からお分か
  りいただけたでしょう。

  7.  次に行うこと

  この文書において、RTLinux におけるプログラミングの基礎について一通り解
  説しました。一度基本概念を理解してしまえば、自力で先に進むのは難しくあ
  りません。したがって RTLinux のソースとともに入手できる他のすべての例
  プログラムを調べることができるわけです。そうすれば、モジュールを書き、
  テストを行うことが可能になるはずです。モジュールのプログラミングに関す
  る情報がもっと欲しい場合は、Ori Pomerantz による 'Linux Kernel Module
  Programming Guide' を参照するのが良いでしょう。

  8.  日本語訳について

  日本語訳は Linux Japanese FAQ Project が作成しました。翻訳に関するご意
  見は JF プロジェクト <JF@linux.or.jp> または、yomoyomo
  <ymgrtq@ma.neweb.ne.jp> 宛に連絡してください。

  本日本語訳について、誤訳・誤記の指摘をして下さったつぎの方々に感謝しま
  す(50音順)。

  o  中野武雄さん <nakano@apm.seikei.ac.jp>

  o  山口つかささん <tyamagch@bd.mbn.or.jp>

  o  山下義之さん <dica@eurus.dti.ne.jp>

