(第33回)割り込み処理を改造する

2009/01/12

あなたは 人目のお客様です.

KOZOSの改良なのだけど,まずは割り込みまわりを大幅に改造したい.

現状でKOZOSは,割り込み処理用に子プロセスを起動して, ソケットの select() を子プロセスにまかせている. ただしソケットは fork() 時に引き継がれるので, 新しくソケットを生成した場合には,そのソケットは子プロセスから見れない. この対策として,ソケットを開くたびに子プロセスを終了,再起動している.

また,ソケットの生成や操作は基本的に各サービススレッド(telnetdとか)が 勝手に行っており,子プロセスは select() 待ちと受信時の通知のみ行っている. accept()に関しても,子プロセスは通知をするだけで,実際の accept() は デーモンが各自行っている.まあ疑似OSという考えで,ソケットまわりは あまり深く考えずに実装してしまったのでこんなことになっているのだが, ちょっとあんまりな作りだ.

そもそもextintrや子プロセスは,ハードウエアの割り込み処理を疑似的に実現するのが 本来の目的なので,ソケット周りの処理はすべて子プロセスにまかせて, 各サービススレッドは文字列の受信割り込みと実際の送受信くらいが行えればよい. つまり,子プロセスが単なるシリアルコンソールのように見えてくれると, それっぽくていい感じだ.

ということでソケットのオープンやaccept(),select()類はすべて子プロセスに まかせるように改造する. 各サービススレッドは,子プロセスから受信文字を受け取ったり, 子プロセスに送信処理を依頼することで,文字データの送受信を行う. (実際には子プロセスとのやりとりのために,extintrスレッドが中継動作をしている)

ソケット関連の処理はすべて子プロセスに閉じて行われるので,各サービススレッド は,外部とのやりとりがソケットで行われていることは意識しない. つまり,ソケットでなくシリアルを利用するように子プロセスを書き換えれば, サービススレッド側は何の変更もなく,telnet接続からシリアル接続に変更する ことができる.

子プロセスは文字の送受信処理と割り込み発行を行うハードウエアを模している. 通常の組み込みOSで,一番手軽な入出力デバイスはシリアルコンソールだ. ソケット処理をすべて子プロセスにすべて任せ,ソケットを意識させないことで, シリアルデバイスを模しているわけだ.

で,子プロセスとどうやって通信するかなのだが,これは専用のソケットを 開いてしまうのがいいだろう. (外部からの接続のためのソケットと,内部通信用のソケットの違いに注意. ここで言っているのは内部通信用のソケットのこと)

通常の組み込みOSだと,これは(おそらくメモリ上にマッピングされた) ハードウエア操作用のレジスタを読み書きすることになるのだが, KOZOSでは親プロセスと子プロセスの間でソケット通信することで, 子プロセス(疑似シリアルデバイス)を操作する.

子プロセスは外部に対してソケットをオープンし,待ち合わせる. 外部からTCP接続要求を受けた際(つまり,kozosに対してtelnetした際)には 子プロセスは accept() する.また文字データを受信した際には, 受信データの通知用ソケットを通じて親プロセスにデータ送信し, 親プロセスに対して SIGHUP を通知する. 親プロセスはSIGHUPを受けたら,なんらかのデータを外部から受信したとして, 受信処理を行う.

つまりKOZOSでは,以下の表のように組み込みOSの動作を模していることになる.

組み込みOSKOZOS
シリアルケーブルによる接続TCP/IPによる子プロセスへの接続(telnet接続)
シリアルからの受信telnet接続からの受信
シリアルへの送信telnet接続への送信
受信割り込み子プロセスから親プロセスへのSIGHUP
レジスタリードによる受信データの読み込み内部通信用ソケットからの受信データ読み込み
レジスタライトによるデータ送信内部通信用ソケットへの送信データ書き込み

で,改造したソースは以下のような感じ.

(2009/04/10 ライセンスに関する文書として,KL-01とLICENSEを追加. 詳しくは第43回を参照)

まず親プロセス側(KOZOS本体側)ではソケットは一切扱わないので, telnetd スレッドは command スレッドに変更.httpdは廃止. command スレッドでは,ソケット処理は行わずに extintr から通知された受信データを 扱うように修正している.

さらに extintr.c は,上述した作りにごっそり書き換えている. 扱うソケット数は決めうちで4個,ポート番号はこれも決めうちで 12345〜12348としている.外部からはこのポートに(telnetなどで)TCP/IP接続できる.

あとGDB利用を考えるとまたややこしいので,stubdはとりあえず廃止.

今回の修正はほとんど extintr に対するものとなっている. 処理を簡単に説明すると,まず子プロセスの fork() 時には親プロセスとの間に 連絡用のソケットを開く. ソケットはコマンドによる子プロセスの操作,受信データの通知,送信データの通知 用に3種類開いている.これを子プロセスが接続管理する4ポートぶん開いて いるので,それだけでも12本のソケットをオープンしている. (さらに外部との接続待ちや接続,全体処理用などのためにソケットを開いている)

command スレッドは,使用したい外部ソケットの番号を指定してuコマンドを extintrスレッドに発行する(このコマンド発行の実体は,KOZOSのメッセージである). extintrスレッドはuコマンドを受信すると,コマンドの発行もとのスレッドをおぼえて おいて,子プロセスからデータ受信した際に,受信ソケットに対応するスレッドに 対して(KOZOSのメッセージで)データ送信を行う.

図にするとこんな感じ.

(初期化時)

  サービススレッド     extintr        子プロセス     外部からの接続
(commandスレッドなど)                               (外部からのtelnet接続など)
         |                |                |                |
         |                |             socket()            |
         |                |                |                |
         |   uコマンド    |             listen()            |
      kz_send()=========>|                |                |
         |             kz_recv()        select()待ち        |
         |                |                |<-----------connect()
      kz_recv()待ち    kz_recv()待ち    accept()            |
         |                |                |<------------>|
         |                |                | TCP/IP接続確立 |
         |                |                |                |
         |                |             select()待ち        |
         |                |                |                |

---> は,ソケットによる接続や通信
===> は,KOZOSのメッセージ送信
(文字データ受信時)
  サービススレッド     extintr        子プロセス     外部からの接続
(commandスレッドなど)                               (外部からのtelnet接続など)
         |                |                |                |
      kz_recv()待ち    kz_recv()待ち    select()待ち        |
         |                |                |                |
         |                |                |<------------send()
         |                |              read()  データ送信 |
         |                |                |                |
         |                |<------------write()            |
         |                |<======------SIGHUP             |
         |             kz_recv()(※1)     |                |
         |              read()       割り込み無効化(※2)   |
         |                |                |                |
         |<===========kz_send()        select()待ち        |
      kz_recv()           |                |                |
         |             kz_recv()待ち       |                |
         |                |                |                |
         |   eコマンド    |                |                |
      kz_send()=========>|                |                |
         |             kz_recv() eコマンド |                |
         |             write()----------->|                |
         |                |              read()             |
         |                |          割り込み有効化         |
         |                |                |                |
         |                |             select()待ち        |
         |                |                |                |

※1 kz_setsig() システムコールにより,シグナル受信は
     メッセージによりextintrに通知される.
※2 データ受信の際に当該の外部ソケットの割り込み無効化し
     (具体的には,select() 用の fd_set のビットを落とす),
     eコマンドによる通知で再度割り込みを有効にする.
(文字データ送信時)
  サービススレッド     extintr        子プロセス     外部からの接続
(commandスレッドなど)                               (外部からのtelnet接続など)
         |                |                |                |
         |             kz_recv()待ち    select()待ち        |
         |     wコマンド  |                |                |
      kz_send()=========>|                |                |
         |             kz_recv()           |                |
         |              write()---------->|                |
         |                |              read()             |
         |                |              write()---------->|
         |                |                |              read()
         |                |                |                |
データの受信時には,受信した外部ソケットを select() 時の fd_set から 落とすことで,一時的に受信検出できないようにする. これは実際のシリアルデバイスで,データの受信時に割り込みを一時的に無効化する ことを模しているつもり. 処理スレッド(commandスレッド)側では受信データをリードしたらeコマンドを 発行することで,受信割り込みの蓋開けをする.

で,extintr.c では以下の関数がそれぞれの処理を行っている.

うーんちょっとややこしいけど,以前よりはすっきりした構造になったような.

ハードウエアが行うような処理は子プロセスのほうにまとめたので, 子プロセスを疑似ハードウエアとして扱うことができるようになっている. KOZOS側では,(操作する先がメモリマップドレジスタかソケットかの違いはあるが) 基本的にはいわゆる普通のシリアルコントローラを扱うような感じで 子プロセスを操作できる. このため,KOZOSをそのままどこかのハードウエアに移植して, 本当のシリアルデバイス利用するような場合にも, extintr.c をシリアルデバイス利用に書き換えるだけで対処できるはずだ.

動作確認についてはめんどうなのでここでは省略するが, とりあえず確認はとれている.次の課題は割り込みの優先度づけかなあ...


メールは kozos(アットマーク)kozos.jp まで