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

もう年末ですね! でも仕事が終らん!

で,前回からちょっと間が開いてしまっているのだが,リアルタイム性をどうやって確保するか,実はちょっと思いあぐねている.

リアルタイム性を確保するためには,OSの処理中の割り込み,つまり割り込みの再入を可能にしたい.前回は割り込みハンドラを登録可能にしたが,とくに割り込みハンドラの実行中の割り込みを可能にしたい.というのは,割り込みハンドラの実行中は割り込み禁止にしてしまうと,ハンドラでなにか重い処理をしたときに,リアルタイム性が確保できないからだ.

しかし,ある重要な割り込み処理をしている最中に,どーでもいいような割り込みが上がってくるのも困る.こーいうような場合には割り込みを待たせたい.でないと重要な処理が中断されて,どーでもいい割り込みの処理に切り替わってしまうからだ.

つまり,割り込みにも優先度を持たせたいということになる.たとえば現状のKOZOSでは外部割り込みは SIGHUP の1種類のみだが,複数種類にして優先度をつけるとか,SIGALRM はもっと優先度を高くするとかだ.割り込みに優先度をつけるのは,シグナルマスクを利用することで段階的にマスクをかけることで実現可能だ.

しかし,割り込みハンドラでの処理は極力短くするのがOSの鉄則だ.ということで割り込みハンドラでは,割り込みを上げてきたハードウエアのバッファを見て(KOZOSならば,extintr が read() することに相当する),それを処理するスレッドにメッセージとして投げてやる,という設計が良い.

しかし,例えば重要な割り込みが入った際に,ハンドラがそれを受けて処理用スレッドにメッセージとして投げたとしよう.スレッドはそのメッセージを受けて処理を開始するが,この際にどーでもいいような優先度の低い割り込みが入ると,やっぱりそのスレッドは待たされてしまうことになる.つまり,優先度の高い割り込みの実際の処理をスレッドに任せることは不可能(他の優先度の低い割り込みに割り込まれてしまうので)ということになる.

これを防止するためには,スレッドの優先度と割り込みの優先度を関連づけて,たとえば優先度の高いスレッドの実行中には,それに応じた割り込みマスクをかけて,優先度の低い割り込みは受け付けないようにする,という方法がある.

もう一点,OSの処理中の割り込みを受け付けるかどうかなのだけど,以下のいずれかの対処をすることでこれは可能だ.
  • KOZOSの内部処理を再入可能にする.
  • 再入不可の箇所は,部分的に割り込み禁止にする.(割り込み禁止区間を設ける)
  • 割り込みの優先度をうまく設計することで,再入不可な関数への再入が実際には起こらないようにする.
で,KOZOSの処理中の割り込み,つまり割り込み中の割り込みを可能にしたとしよう.割り込み中にもっと優先度の高い割り込みが入り,そっちのハンドラが新たに呼ばれたとする.優先度の高い割り込みのハンドラはとりあえず処理用のスレッドにメッセージを投げて,あとはスレッドに処理を任せるとしよう.しかし,ハンドラの終了後にすぐにスレッドをディスパッチすることはできない.もともと行っていた(優先度の低い)割り込み処理にいったん戻り,処理を完了させてからディスパッチしなければならないのだ.でないともともとも割り込み処理は途中までの中途半端な位置まで実行されただけで,宙に浮いてしまう.

割り込みを階層的にした場合には,何十にもネストして発生する可能性もある.ということは,優先度の高い割り込みのハンドラがメッセージを投げて,さらにその処理用スレッドがディスパッチされるまでの時間が保証できなくなってしまう.どれくらいの割り込みがネストしていて,それらの処理にどれくらいの時間がかかるのか見積もることができないからだ(いや正確に言うと最悪時間を見積もることはできるのだけど,きっととんでもなく長い時間になってしまう).

ということで,結局のところリアルタイム性を確保するには割り込みハンドラ内ですべての処理を行うか,もしくはKOZOSの内部処理中の割り込みは禁止にして,現状のように内部処理が完了した時点で割り込み許可するかのいずれかになってしまう.前者は融通が効かなそう.後者は応答が悪そう(あと割り込み禁止区間が長くなるので,リアルタイム性を保証するのが難しくなりそう).

うーん,困った.ということでいまちょっと本を読んで勉強中なのだな.なんかうまいやりかたはないだろーか.

きれいな解決策としては,ハンドラごとにコンテキストを持たせるというか,ハンドラをスレッド化するという方法がある.実はKOZOSはもともとそんなようなつくりになっているのだけど,割り込みハンドラを単なる関数呼び出しにするのではなく,kz_setsig() によりメッセージを投げさせて,完全にスレッドとして処理させるというものだ.これならきちんと優先度をつけて処理することが可能だ.可能なのだが,うーん,でも割り込みのたびにディスパッチされるというのもねえ...きれいだけど,なんか性能悪そうでやなのよね.

あーでも,KOZOSの内部処理中に割り込み入ったらすぐにスレッドをディスパッチできないから同じことか.やっぱダメだね.