OS自作入門 -Advent31-

NO IMAGE

Step8 スレッドを実装する 最終戦

引き続き残りのスレッド管理周りを実装していくことにしよう。

OSの実装(続き)

組み込みハンドラの登録

kozos.cの続きに組込みハンドラを実装していく。KOZOSはソフトウェア・割込みベクタに対するハンドラをhandlers[]という配列に持っており、割込み発生時にはそのハンドラを実行する。

ここで定義しているsetintr()関数はhanders[]にハンドラを設定するためのサービス関数となる。

softvec_setintr()をソフトウェア・割込みベクタにOSのハンドラとしてthread_intr()を登録している点がちょっと分からなかったな。

これにより、指定した割込みが発生した際にはthread_intr()が呼ばれ、OSに処理が渡るようになります。

という記載があるが、、、thread_init()の実装の方でOSとしてスレッドを実行するような処理が書かれるのだろうと推測。

システム・コールの実行

追加の変更分はここ

システム・コールの発行時にはgetcurrent()によりカレントスレッドがレディーキューから取得されてから、call_functions()が呼ばれ、システムコールの種別に応じて処理が実行される。

割込み処理

割込み処理はこの辺り

上述の通り

これにより、指定した割込みが発生した際にはthread_intr()が呼ばれ、OSに処理が渡るようになります。

という箇所がある。これはsoftvec_setintrが呼ばれ、ソフトウェア・割込みベクタに割込みハンドラとしてthread_intr()が登録される。これにより、割込み発生時にはthread_intr()が呼ばれ、コンテキストとしてスタックポインタを保存し、handlers[]に登録された関数を呼び出しているという処理になる。(handlers[]へのハンドラのセットはここでやっている)

また、ハンドラを呼び出した後はschedule()を呼びスケジューリングを行う。実行するスレッドを選択することをスケジューリングと呼ぶが、優先度高いものとかの処理がある場合はここが多少複雑になるのだろうと思っている。

この後にディスパッチすることによりカレントスレッドが動き出す。ここでスタックポインタの値が格納されているアドレスを渡し、そこからスタックポインタの値を復旧し、さらに各汎用レジスタの値を復旧する動きになる。

初期スレッドの起動

kz_startの実装。ここに置いておく。kz_startが呼ばれることにより、一つ目のスレッドが初期化されて動作を開始する。あとは初期スレッドからkz_runにより各種サービス用スレッドを必要に応じて起動するとOSが出来上がる。また、thread_runによってスレッドを生成し、dispatchでスレッドに実行を移し、初期スレッドが動作を開始した後は、割込みを契機としてOSに処理が渡るようになっている。

システムコール

システムコールのヘッダーファイル
現状kz_runkz_exitの二つ。

typedef enum {
  KZ_SYSCALL_TYPE_RUN = 0,
  KZ_SYSCALL_TYPE_EXIT<
} kz_syscall_type_t;

という形でシステムコール番号を定義している。次にkz_syscall_param_tという構造体を定義している。これはシステムコールのパラメータや戻り値の受け渡しの際に、構造体のポインタで渡せるようにしているためである。引数が複数ある場合があるので、構造体に一旦格納し、そのポインタを渡せるようにしておくための構造体。

システムコールの実装
C言語からKOZOSのシステムコールを呼び出すためのサービス関数でしかない。APIと呼ぶらしい(いつも使っているAPIはかなり広義かもしれん)。

APIはアプリケーションプログラムがOSの機能を利用する際に利用するインターフェースです。

とのこと。

OSで利用する型の定義

defines.hに置いてある。

  • kz_thread_id_t
    スレッドIDを表す型
  • kz_fund_t
    スレッドのメイン関数を扱うための型
  • kz_handler__t
    割込みハンドラなどを扱うための型

OSを利用する

さて、ようやくOS周りの実装が終わったので利用してみよう。

main.cに置いてある。

kz_start()を呼んで初期スレッドを起動し、あとはスレッドに動作が渡るだけ。

  kz_start(start_threads, "start", 0x100, 0, NULL);

それぞれ、"start"はスレッドの名前、0x100はスタックサイズ、0, NULLはメイン関数に渡すargc, argvの引数。

start_threadsの中ではtest08_1_mainを実行しており、これがスレッドとして動作開始することになる。

アプリケーションプログラムはtest08_1_mainなので実装する。test08_1.cに置いてある。あまり前と変わっていない。echo, exitを受け付けてそれぞれ動作するようになっている。

最後にMakefilekozos.c, syscall.c, test08_1.cをコンパイル対象に含めて終わり。

実行

失敗する!!!なぜだ。

エラーログは

tools/bin/h8300-elf-gcc startup.o main.o interrupt.o lib.o serial.o kozos.o syscall.o test08_1.o -o kozos -Wall -mh -nostdinc -nostdlib -fno-builtin -I. -Os -DKOZOS -static -T ld.scr -L.
# kozos.o: In function `.L13':
# kozos.c:(.text+0x175): undefined reference to `___mulsi3'
# kozos.c:(.text+0x189): undefined reference to `___mulsi3'
# kozos.c:(.text+0x1a3): undefined reference to `___mulsi3'
# kozos.c:(.text+0x1b9): undefined reference to `___mulsi3'
# kozos.c:(.text+0x20d): undefined reference to `___mulsi3'
# kozos.o:kozos.c:(.text+0x275): more undefined references to `___mulsi3' follow
# collect2: error: ld returned 1 exit status
# make: *** [kozos] Error 1

困った。。。意味が分からん。___mulsi3が見つからないよ。ってのは分かるがそれがなんで見つからないのかが分からん。kozos.c

  thp->next = NULL;

  thp->init.func = func;
  thp->init.argc = argc;
  thp->init.argv = argv;

  memset(thread_stack, 0, stacksize);
  thread_stack += stacksize;

  thp->stack = thread_stack;

この辺りをコメントアウトするとエラーが消える。多分thpが見つからないか、ポインタ参照で見つからないか。かなぁとか思っているけど理由が分からなかった。

が、調べると神々がいるもんだ。

http://d.hatena.ne.jp/satfy/20110202/1296666655
http://kozos.jp/kozos/h8_2/h8_04/os/kozos.c

助かった。。。危うく積んだかと思った。理由はちょっと分からないけど構造体のサイズの問題みたい。
padding(何かで埋めておく、この場合char dummy[16]で埋める)すると大丈夫みたい。

bootloadの方をmake & make image & make writeして、osの方をmake & ../../tools/bin/kz_xmodem kozos.elf /dev/tty.usbserial-XXXXXXして、sudo cu -l /dev/tty.usbserial-FT0BTH3I -s 9600とすると以下のような感じになる。

Connected.

unknown.
kzload> run
starting from entry point: ffc020
kozos boot succeed!
start EXIT.
test08_1 started.
> echo hogehoge
 hogehoge
> abc
unknown.
> exit
test08_1 exit.
command EXIT.
system error!

これでいいはず。最後のsystem error!は処理するスレッドがないから出る表示。


今日はこんな感じで。なんか一番難しかったような気もする。あとある程度写経しているのだが、コンパイルとか実行をこまめにやるわけではなく、最後の最後にまとめてコンパイル、リンク、write、loadとかするので書き間違えるととても探すのに時間がかかるなぁ。

この本読み終えたら一回総集編を作らないと知識の整理が追いつかない。