Download追加 2005/07/16
システムコールの簡易リアルタイム化追加 2006/07/24
kernel-2.6.15への移行追加 2006/10/22
Debian Flash Memory版追加 2006/10/31
  目  次
1 はじめに
2 概  要
3 プログラミング条件
4 機能説明
5 動作及びソース
6 結 果
7 ドライバ内からのシステムコール
8 Download
 はじめに
 実際に現場にいると、単に早いレスポンスを要求するためLinux等の汎用システムが使えない場合が多い。言い換えれば、間に合えば良いシステムは数多くあるようだ。このようなシステムでLinuxを使う一方法を述べる。
このような場合、デバイスドライバで処理するのが一般的と思われる。このデバイスドライバ内で簡単なシーケンスプログラムを組むのは容易ではない。ところがRT−Linux等は、この常識を破ってくれたので、次のようなシステムを作成してみた。
 ○ 音声処理等の早いレスポンスを要求する処理をターゲットとする。
 ○ タスクの実現にはKernelThreadを使用する。
 ○ 従って、タスクスイッチはLinuxのKernelThreadコントロールに依存する。
 ○ その意味でSemiRealtimeである。
 ○ マルチタスクの最低限の機能としてセマフォとメッセージキューをサポートする。
 ○ 1msecからLinuxSystemTimeまでの時間分解能を目標とする。
 概  要
 さし当たって作成した関数であり、論理的には抜けている機能が数多くあるが、実用上差し支えない程度に絞ってある。(手抜きとも言う。 例えばIntBindに対するUnbindとか。 一旦開始してしまえばシステム終了するまで動作すると仮定している。)
 主な機能は次の通りである。
itIntBind 割り込みの設定及び割り込みハンドラの設定
itIntUnbind 割り込み設定の解除
tIntLock 割り込み禁止
itIntUnlock 割り込み禁止解除
itThreadCreate デバイスドライバ内でのThreadの作成
itThreadPriority thread priorityの設定
itThreadStart thread の実行開始
itTaskDelay 当該threadの遅延
itSemInit セマフォの作成
itSemTake セマフォを取得する
tSemGive セマフォを与える
itMessageCreate メッセージキューの作成
itMessageSend メッセージ送信
itMessageReceive メッセージ受信
 プログラミング条件
プログラミングはデバイスドライバ作法に依る。
タスク(thread)のスケジュールはKernelがサービスする。従って、RealTime性能は期待できないが、割り込みとの併用で素早い動作が期待できる。
なお、kernel2.6への移行により、kernel-preemptionが1msecのシステムクロックと相まって、あるレベルのRealTime性を持たせることができる。
 機能説明
4・1 割り込みの設定及び割り込みハンドラの設定
int itIntBind (int vector, void(*routine)(void*), void *param)
vector in int 割り込みベクター
routine  in void (*routine) (void*) 割り込み処理ルーチン
param in void * 割り込み処理ルーチンへの引数
4・2 割り込み設定の解除
int itIntUnbind (int vector)
vector  in int 割り込みベクター
戻り値   int  
4・3 割り込み禁止
int itIntLock ( )
戻り値   int コール前のflags
 システムを割り込み禁止とする。
4・4 割り込み禁止解除
void itIntUnlock (int flags)
flags in int itUntLockの戻り値
 flags :  itIntLockの戻り値
4・5 デバイスドライバ内でのThreadの作成
int itThreadCreate (int *tid, struct it_thread_attr *attr,
     void *entrypoint, void *arg)
tid out int * threadのID
attr in struct it_thread_attr* threadのアトリビュート(priority)
trenypoint in void * threadのエントリーポイント
arg in void * 引数へのポインタ
戻り値   int 0:正常  -1:失敗
      ThreadのPriorityの設定
int itThreadPriority (int *tid, unsigned long priority)
tid in int * threadのID
priority in unsigned long threadのPriority H(0〜39)L
戻り値   int 0:正常  -1:失敗
       Threadの 実行
int itThreadStart (int *tid)
tid in int * threadのID
戻り値   int 0:正常  -1:失敗
4・6 当該threadのスリープ
void itTaskDelay (int msec)
msec in int threadのスリープ時間(msec単位)
 msec : threadのスリープ時間(msec単位)
4・7 セマフォの作成
int itSemInit (int val, struct semaphore *sid)
val in   0
sid out struct semaphore * semaphoreハンドルへのポインタ
戻り値   int  
4・8 セマフォを取得する
 セマフォが「empty」の(取得できなかった)場合はタスクは待ち状態にされる。「full」の(取得できた)場合及びタイムアウトでコントロールが戻る。
int itSemTake (struct semaphore *sid, int timeout)
sid in struct semaphore * itSemInitで取得したsid
timeout in   タイムアウト時間
戻り値     0:正常 -1:タイムアウト
4・9 セマフォを与える
 既にこのセマフォに対して待ち状態あるタスクを実行状態にする。
もし 待ちタスクが無い場合は、このセマフォを「full」状態とする。
Int itSemGive (struct semaphore *sid)
Sid in struct semaphore * itSemInitで取得したsid
戻り値     0:正常
4・10 メッセージキューの作成
 メッセージキューを作成しmsghを返す。キューの数及びメッセージの長さは固定です。
メッセージの長さはこの値を超えないこと。
struct msgQhandle *itMessageCreate ( )
戻り値   struct msgQhandle  msgh メッセージキューのハンドル
内容要約
struct msgQhandle *itMessageCreate()
{
    struct semaphore **sidp;
    struct msgQhandle *msgQhd; 
    int status;
    msgQhd = getMesgQh(); 
    sidp = &msgQhd->sid; 
    status = itSemInit(1, (struct semaphore *)sidp);
    return msgQhd;
}
4・11 メッセージ送信
 メッセージキューにメッセージを送り込む。
待ち状態にあるタスクは実行状態に戻されます。
int itMessageSend (struct msgQhandle *msgh,
    void *message, int len)
msgh in struct msgQhandle* itMessageCreateで取得したmsgh
message  in void * メッセージ本体
len  in int メッセージ長
戻り値     0:正常
内容要約
int itMessageSend(struct msgQhandle *msgQh, 
                       void *message, int len)
{
    if (!itSemGive (msgQh->sid)) {
        putQ(msgQh, message, len);
        return normal;
    }
    return fault;
}
4・12 メッセージ受信
メッセージキューからメッセージを取得する。キューが空の場合待ち状態に移行する。
待ち状態の場合、新にメッセージがキューに入るとコントロールが戻り実行状態になります。
int itMessageReceive (struct msgQhandle *msgh,
    void *message, int len, int timeout)
msgh in struct msgQhandle* itMessageCreateで取得したmsgh
message  out void * メッセージ本体
len in int 最大メッセージ長
timeout in int タイムアウト時間(msec)
戻り値     0:正常 -1:タイムアウト
内容要約
int itMessageReceive (struct msgQhandle *msgQh,
                  void *message, int len, int timeout)
{
    if (!itSemTake(msgQh->sid, timeout)) {
        getQ(msgQh, message, len);
        return normal;
    }
    return fault;
}
 動作及びソース
ちょっとテストプログラム
 どうせならとkernel-2.6.15-1.2054_FC5へ移行した。kernel-2.6ではHZ=1000とkernel-preemptionがサポートされ、それだけでリアルタイムが実現できそうな勢いですが、実際はそうはいかないようです。
ここではkernel-2.6.15-1.2054_FC5のHZを1000に変更するのみのkernel-2.6.15-prepを作成し使用した。 使用マシンはPentium4-1.5Mhz、ASUS P-4B。
5・1 タイマーループの精度
 標準のPC互換機では空いたタイマー割り込みが無いので、システムタイマーとしてシステムクロック(HZ)を使用することになる。従って、タイマー精度というよりは、Voluntary Kernel Preemptionが単位システムタイム(HZ)で制御を得られるかのテストになる。
 他のタスクを同時に動作させた場合でも±100usec以内に収まるようです。 当然ながら遅延時間の分解能はHZにより決まり、この場合1msecです。ということは、他のCPUで割り込みで行っている遅延ループと異なり、1msecは正確でありHZをとり逃さない限り 時間誤差が累積することは無い。
 ここでは、この1mesecに対してどのくらいふらつくかが焦点になる。
5.2 サウンド入出力 システムコール
    ( kernel-preemptionとは共存不可)
 サウンドは/dev/dspおよび/dev/audioのfragmentが被テスト機では最小で1024bytesまでである。 これは8khz、16bitMonauralで64msec分であり、リアルタイムテストには不向きであるが10msec分のデータ毎の処理をしてみた。 まあカーネル内でこんなこともできますよという例にしかなりませんが。
 これは意味が無いので時間を計測していないが、音声は途切れることなく動作する。 というのは、Audio入力は1024バイト溜まらないとデータが出てこないし、Audio出力は1024バイト溜まらないとスピーカに出力しない。 即ちバッファ1024バイトある格好になるので、スムースに動作するのは当たり前ですが・・・。 一方サウンドドライバに潜り込んでいる時間が長くなり、そのため他のタスクの起動時間のゆれは100usec程度なることもある。
 なお、VIA Ezra 665Mhz上でのUSBヘッドセット(/dev/dsp1)ではfragmentは最低16バイトであった、8khz、16bitMonauralでは 1msecに相当する。 Via665Mhzでのテストではaudioによる他のタスクwakeupのふらつきは10usec以下になっている。
5.3 UDPシステムコールのテスト
    (kernel-preemptionとは共存不可)
 以前から制御データを遅滞無く送信するのに苦労していた。これをデバイスドライバ内から直接送信できれば安定した送信が期待できる。
 結果は5.1と同様に1msec±100usec内の精度で送信が実行されている。おそらく外部割込みで動作しているようなタスクで、外部イベントに同期して通信するものには100usec以内の時間精度で送信できる可能性がある。
 結 果
 実際動作させた場合、(pentium4-1500Mhz)CPUの負荷状況等によりますが、1msec以上の周期でタスクの起動は問題なく動作する。 特にマルチタスク的な動作を要求するアプリケーションではプログラムがすっきり出来上がるので重宝に使っています。
 ただし、デバイスドライバと同様の注意は必要です。
 ドライバ内からのシステムコール (kernel-preemptionとは共存不可)
 より早いレスポンスを得るためにはカーネル内からシステムコールを呼べれば、より優先度が高くオーバヘッドも少なくレスポンスは良いはずです。
 そこでシステムコールのうちopen、read、write、ioctl、closeをカーネル内から呼べるサービを追加した。 しかし、kernel-2.6のkernel-preemptionとは共存できません。

int it_open(const char *pathname, int flags, mode_t mode);
pathname in char* パスネーム
flags in int フラッグ
mode in mode_t mode
戻り値   int ファイルディスクリプタ

ssize_t it_read(int fd, const void *buf,size_t count);
fd in int ファイルディスクリプタ
buf in void* 読み込みバッファ
size in size_t 最大読み込み長
戻り値   int 実読み込み長

ssize_t it_write(int fd, const void *buf,size_t count);
fd in int ファイルディスクリプタ
buf in void* 書き込みデータ
count in size_t 書き込むべきデータ長
戻り値   int 実書き込み長

int it_ioctl(int fd, unsigned long request, char *argp);
fd in int ファイルディスクリプタ
request in unsigned long IOCTLのコマンド
argp in char* dataへのポインタ
戻り値   int 0:正常 -1:エラー

int it_close(int fd);
fd in int ファイルディスクリプタ
戻り値   int 0:正常 -1:エラー
 また懸案であったカーネルからのソケットの呼び出しも、socket、 setsockopt、 getsockopt、bind、 sendto、recvfromのサービスを追加した、UDP通信に威力を発揮する。ソケットは他にも使われることも多くサウンド等と異なり専用になり難 いため、音声/映像等のRTP(UDP)通信を除いて濫用は避けるべきであろう。
 DOWNLOAD (FedoraCore5)
 使用法
デバイスドライバ mudrvtest.tgz と testapp.tgz をダウンロードして各々適当なディレクトリに解凍する。 makeすればドライバモジュールとテストアプリが出来上がります。
とはいえ、kernel-2.6になってから、使用するkernel-2.6.xxxをコンパイルした環境でドライバモジュールを作成しなければならなくなったようです。
 従って、予めkernel-2.6.15-1.2054_FC5のソースを取得する。
  • make menuconfig で processor type and features のTimer frequency を
    1000HZ にチェックする。
  • make, make modules, make install, make modules-install を実行し kernel-2.6.15-prepを作成しておく。
  • このディレクトリ kernel-2.6.15-prep を用いてドライバモジュールを作成する。
  • kernel2.6 からModule名はxxxxxx.ko に変わっているので取り扱う toolutility も
    対応していなければならない。 FC5であれば問題ないが。
8・1 Via Ezra のフラッシュメモリ
 まず、基になるDebian3.1-kernel-2.6.8-3-1386の最小システムを ここ から持ってくる。
内容はDebianのインストールを可能とする最小のシステムのみが入っています。(注意)正味256Mbあります。
 CFはbuffalo RCF-X 256MBを対象にして、IDE(1,1)2番目のチャンネルのマスターにアサインしています。
 ダウンロードしたファイルはCFのイメージそのものですから、書き込み可能なLinuxマシンにてddコマンドでCFに書き込んでください。(これはターゲットでは/dev/hdcになります。)
 なお、"root"のpasswdおよびuser"admin"のpasswdは共に"admin01"です。
 audioは組み込みのものを使わないでUSBヘッドセット(/dev/dsp1)を使用します。
  • テストドライバはDebian3.1-kernel-2.6.8-3-i386用にコンパイル済みが ここ
    用意してあります。
  • テスト実行アプリも ここ に用意してあります。
実行は
mknod /dev/mudrv c 251 0 デバイスファイルを作成
chmod 666 /dev/mudrv
insmod test.ko ドライバモジュール
simplert テストアプリを実行

質問は こちら から



Linuxで簡易なマルチタスク・ドライバ(2)T-Engineとsh7760のサウンド・ドライバ
RT-Linuxデバッグの一方法