(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

前回の続きで,割り込みまわりを改造していきたい.今回はタイマ割り込みだ.

タイマ処理についてなのだが,現状では各スレッドが kz_timer() システムコールによって自由にタイマ設定できる.KOZOSの内部では,タイマキューを検索して適切な位置にタイマ登録し,タイマキューの先頭に対して getitimer() で SIGALRM をかけている.実際のハードウエアでは,SIGALRM はリアルタイムクロック(RTC)からのタイマ割り込みということになる.

このようなタイマキューの使いかたは,タイマ処理をしたい場合の定石の書き方だ.タイマキューを使うことで,ひとつしかないタイマ資源(KOZOSの場合は,SIGALRMによるシグナル)でいくつものタイマをかけることができる.

ただしこれはリアルタイムOSとして考えると,あまり良い方法ではない.タイマ設定時に必ずタイマキューの検索が入ってしまうため,リアルタイム性を確保できないからだ.

で,リアルタイムOSではどのようにするかなのだけど,ちょっといろいろ考えたのだけど,本当に厳密なタイマ処理をしたい場合には,タイマキューのような仕組みはNGだ.しかし,各スレッドがちょっと数秒単位でのタイマをかけたい場合には,厳密なタイミングやリアルタイム性が必要ではない場合も多い.

ということで結論としては,厳密なリアルタイム性が必要なタイマは,ハードウエアが持っているRTCをそのまま使うしかない.たとえばハードウエアが4つのRTCを持っているならば,タイマ資源を4つのみに限定して,タイマを必要とするスレッドは,RTCを直接利用しまえば,リアルタイム性を確保できる.

ただしこれでは,タイマは4つまでしか利用できないので,他のスレッドがすでにタイマを利用していると,もうタイマを使うことはできなくなってしまう.ということで,4つのうちのひとつだけはタイマキューを実装して,精度を必要としないスレッドは,タイマキューのほうのタイマを利用すれば,タイマはいくらでも使うことができることになる.

この場合,厳密なリアルタイム性を持つタイマとして,3つのタイマ資源を利用できる.またそれとは別に,あまり厳密でない(そしてリアルタイム性も無い)タイマを(こちらはキュー管理しているので,論理上は無限に)利用できることになる.

ついでにタイマキューの処理はリアルタイム性を確保できない(タイマキューの検索があるので).このためタイマキューの処理は専用のスレッドに任せてしまうのがいいと思う.

結局どのような構造になるかというと,たとえばタイマ資源としてRTCを4つ搭載しているようなハードウエアならば,以下のような感じだ.
  1. 4つのタイマ資源(RTC)のうち,3つは一般スレッド用に解放する.このためユーザスレッドは,リアルタイム性のある(厳密なタイミングで動作できる)タイマを全体で3つまで利用できる.
  2. 4つのRTCの残りのひとつは,タイマ管理スレッドが利用する.タイマ管理スレッドはタイマキューを持ち,,RTCを利用してタイマ動作することで,ユーザスレッドにタイマサービスを提供する.
  3. 厳密なタイミングを必要とする場合は,上記1のタイマ資源を利用する.これは有限の資源なので,あるスレッドがすでに利用していたら,他のスレッドは利用できないことになる.
  4. 厳密なタイミングを必要としない(数ミリ秒ずれても構わない)場合は,上記2のタイマサービスを利用する.タイマ登録は,タイマ管理スレッドにメッセージを投げることで依頼する(タイマ満了でメッセージが返ってくる).タイマ管理スレッドはタイマキューの検索を行うためその動作にリアルタイム性は無いが,スレッド化してあるのでOSのリアルタイム性への影響は無い.
つまり,リアルタイム性を確保できない部分(タイマキューの処理)はタイマ管理スレッドに任せてOSの外に出すことで,OSのリアルタイム性に悪影響が出ないようにしている.

ひとつのタイマで汎用的なタイマサービスを実現したい場合,タイマをキュー管理することでリアルタイム性が無くなってしまうことは避けられない.このため,リアルタイム性と厳密さが必要な処理に関しては,専用のRTCを割り当てるのが正しい考えだ.

で,extintr.c にタイマ割り込み処理を実装してみた.ソースは以下のような感じ.

ついでにコンソール処理での w コマンドによる出力処理でレジスタの検索を忘れていたバグを修正.(以下)

case 'w': /* 出力 */
+ irp = intrreg_search(id);
write(irp->write_fd, &p[1], size - 1);
break;



タイマ割り込みの処理を図にするとこんな感じ.ちなみに従来の clock スレッドを,新設したタイマを利用するように修正してある.

(タイマ設定と動作時)

サービススレッド extintr 子プロセス
(clockスレッドなど)
| | |
| kz_recv()待ち select()待ち
| | |
kz_send()=========>| |
| kz_recv() |
| write()----------->|
kz_recv()待ち | read()
| | |
| | select()
| | |
| | (タイマ満了)
| | |
| |<------------write()
| |<======------SIGHUP
| kz_recv()(※1) |
| read() |
| | |
|<===========kz_send() select()待ち
kz_recv() | |
| kz_recv()待ち |
| | |

---> は,ソケットによる接続や通信
===> は,KOZOSのメッセージ送信

※1 kz_setsig() システムコールにより,シグナル受信は
メッセージによりextintrに通知される.

extintr.c の処理には,タイマ処理が追加してある.前回実装したソケット通信処理(コンソール処理)に対して,同じような感じでタイマ処理が追加されている.(タイマとコンソールを別の子プロセス&別スレッドにしてもよかったのだが,将来的にタイマとコンソールをひとつの処理で割り込みマスクできるように,extintr.c にまとめてある)

まあ具体的な処理は,前回追加したコンソールの処理とだいたい同じだ.extintrスレッドと子プロセスの間でソケットを張り,ソケット経由でタイマの起動を依頼すると,子プロセスがタイマ動作して満了時にはソケットに通知し,SIGHUPを上げてくる.で,extintr は SIGHUP を受信したらソケットを調べ,タイマ満了している場合には当該スレッドにメッセージを送信する.

あと clock.c は,従来はKOZOSのタイマサービスを利用していたが,extintr が持つタイマを利用するように修正してある.

extintr がタイマ実装されたことで,KOZOSのタイマサービス(kz_timer())は不要になる.まあこの処理はリアルタイム性が無いのでOSの中に残しておくのはまずいので,そのうち削除しよう.あーそれと,汎用的なタイマサービスのためにタイマ管理スレッドを実装する必要があるなあ(KOZOSのタイマ処理をもとにすればいいだろう).まあこれは後ほど.

実行結果は以下.いちおう,ちゃんとタイマ動作しているようだ.telnetによる接続も問題ないみたい.

hiroaki@teapot:~/kozos34>% ./koz
Mon Jan 19 00:30:13 2009
Mon Jan 19 00:30:14 2009
Mon Jan 19 00:30:15 2009
Mon Jan 19 00:30:16 2009
Mon Jan 19 00:30:17 2009
...