さてモジュールとは何でしょうか? Linux のモジュールは、通常 gcc に -c フラグ引数を付けて生成されたオブジェクトファイルに過ぎません。 モジュール自体は、main() 関数のない普通の C 言語ファイルをコンパイル することにより生成されます。main() 関数がない代わりに、init_module 関数と 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
通常リアルタイムアプリケーションは、複数の実行「スレッド」で構成されます。 スレッドは、共通のアドレス空間を共有する軽量のプロセスです。RTLinux では、 すべてのスレッドが Linux カーネルのアドレス空間を共有します。 スレッドを使うことの利点は、コンテキストスイッチに比べて スレッド間の切り替えが極めて軽いことにあります。 以下の例で紹介する各種関数を利用すれば、スレッドの実行を完全に制御できます。
スレッドの動作を理解するのに最も良いのは、リアルタイムプログラムを トレースすることです。例えば、以下に示すプログラムは、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() をコールするのです。