LinuxやWindowsで動かすには?ではLinuxでの動作に ちょっと挑戦してみましたが,失敗してました.
で,いろいろ試してよーやく Linux での動作確認がとれたので, Linux での動作について説明.
まずぼくの Linux 環境ですが,Windows 上で QEMU を動かして作った仮想PCに Fedora Core 8 をインストールして使っています. これは動作が結構遅いのだけど, まあ実験用の環境なので速度はそれほど問わないのでいいかな, という感じで使っています. 遅すぎてダメという人は,QEMU用のアクセラレータというものがあるようなので 使ってみるといいでしょう.
で, LinuxやWindowsで動かすには? では,Linux上で動作させたら Segmentation Fault で落ちてしまい まともに動かなかったのですが,main.c の INTERVAL の値を増やすことで 回避できるようになりました.どうもエミュレータ上での動作のために 動作速度がとっっっても遅かったことが原因だったようです. 予想される原因なのですが,thread.c の thread_intrvec() の終りのほうで スレッドのディスパッチ処理を行う直前に
on_os_stack = 1; sigprocmask(SIG_UNBLOCK, &block, NULL); sigprocmask(SIG_BLOCK, &block, NULL); on_os_stack = 0;のようにして SIGALRM の発生を見ている部分(ここでなぜこーいうことを しているのかは,第14回を参照)で,エミュレータ上での動作が遅すぎるために SIGALRM の設定後に上記処理が行われる手前で再度 SIGALRM が発生してしまい, うーんでも考えてみたら SIGALRM 発生時の処理はスレッドにメッセージ送信 するだけなので,べつに問題ないような気がしてきた.うーん不明. まあそのうちちゃんと考えよう.
とりあえず第24回や第25回のソースをQEMUのようなエミュレータ上や 動作の遅いPC上で動作させる場合には,以下の修正のようにして, INTERVALを調整してください.PCのスペックに対して値が小さすぎると 落ちるようです. ぼくの環境ではINTERVALを100にすることでうまく動きましたが, PCの速度に応じて値を調整してください. INTERVAL をてきとうに増やしていって,しばらく動かしても Segmentation Fault に ならないようになったところで動作させるといいでしょう.
diff -ruN kozos25.orig/main.c kozos25/main.c --- kozos25.orig/main.c 2007-12-05 22:16:58.000000000 +0900 +++ kozos25/main.c 2007-12-08 14:19:44.000000000 +0900 @@ -12,7 +12,7 @@ char buf[128]; char *p = buf; -#define INTERVAL 5 /* CPU能力に応じて調整してください */ +#define INTERVAL 100 /* CPU能力に応じて調整してください */ while (1) { count = 0; kz_timer(INTERVAL - argc);で,さらに Linux 用に以下の修正を入れることで,無事動作することを確認 できました.
上の修正は第23回のソースに対する修正差分なのだけど, 第25回のソースに同様の差分を当てることで(さらにINTERVALを調整することで) 第25回のソースも動作することを確認した. あと上の差分を当てる場合には, LinuxやWindowsで動かすには? で配布している差分は当てる必要がありません. 上の差分だけ当てればよいです.
ちなみに以下が,第23回と第25回のソースコードに対して, Linux 対応を入れたソースです.
(2009/04/10 ライセンスに関する文書として,KL-01とLICENSEを追加. 詳しくは第43回を参照)
オリジナルからの差分は diff.txt にあるので参考にしてほしい.
以下に,修正内容についてちょっと説明.
まず Linux では ualarm() によるタイマの設定は,1000000マイクロ秒未満, つまり1秒未満でないとダメらしいので,その対応.
diff -ruN kozos23.orig/clock.c kozos23/clock.c --- kozos23.orig/clock.c 2007-12-04 22:43:50.000000000 +0900 +++ kozos23/clock.c 2007-12-08 15:01:07.000000000 +0900 @@ -13,7 +13,7 @@ char *p; while (1) { - kz_timer(1000); + kz_timer(999); kz_recv(NULL, NULL); t = time(NULL);clock スレッドの時刻表示を999ミリ秒ごとに行うように修正している. これは本来ならば setitimer() 使うように書き直さなければならないと思うのだけど, まあとりあえずこうしておく. ちなみに FreeBSD だと ualarm() のタイマの上限は 100000000000000 マイクロ秒 なのでとくに問題にはならなかったようだが,まあ Linux 側と合わせて setitimer() 使うようにそのうち修正しよう.
次に,(これはLinuxやWindowsで動かすには?で すでに行っている修正だが)ucontext_t によるコンテキストからスタブ側に レジスタ情報を渡す部分の修正.glibc に合わせて修正している.
diff -ruN kozos23.orig/stublib.c kozos23/stublib.c --- kozos23.orig/stublib.c 2007-12-04 22:43:50.000000000 +0900 +++ kozos23/stublib.c 2007-12-08 14:21:48.000000000 +0900 @@ -3,6 +3,7 @@ #includeさらに,テキスト領域を書き込み化にするための t2w に対して, Linux でコンパイルを通すための修正.t2w については第11回を参照.#include +#define __USE_GNU #include "kozos.h" #include "thread.h" #include "stublib.h" @@ -101,42 +102,42 @@ { memset(registers, 0, sizeof(registers)); - registers[EAX] = thp->context.uap.uc_mcontext.mc_eax; - registers[ECX] = thp->context.uap.uc_mcontext.mc_ecx; - registers[EDX] = thp->context.uap.uc_mcontext.mc_edx; - registers[EBX] = thp->context.uap.uc_mcontext.mc_ebx; - registers[ESP] = thp->context.uap.uc_mcontext.mc_esp; - registers[EBP] = thp->context.uap.uc_mcontext.mc_ebp; - registers[ESI] = thp->context.uap.uc_mcontext.mc_esi; - registers[EDI] = thp->context.uap.uc_mcontext.mc_edi; - registers[PC] = thp->context.uap.uc_mcontext.mc_eip; - registers[PS] = thp->context.uap.uc_mcontext.mc_eflags; - registers[CS] = thp->context.uap.uc_mcontext.mc_cs; - registers[SS] = thp->context.uap.uc_mcontext.mc_ss; - registers[DS] = thp->context.uap.uc_mcontext.mc_ds; - registers[ES] = thp->context.uap.uc_mcontext.mc_es; - registers[FS] = thp->context.uap.uc_mcontext.mc_fs; - registers[GS] = thp->context.uap.uc_mcontext.mc_gs; + registers[EAX] = thp->context.uap.uc_mcontext.gregs[REG_EAX]; + registers[ECX] = thp->context.uap.uc_mcontext.gregs[REG_ECX]; + registers[EDX] = thp->context.uap.uc_mcontext.gregs[REG_EDX]; + registers[EBX] = thp->context.uap.uc_mcontext.gregs[REG_EBX]; + registers[ESP] = thp->context.uap.uc_mcontext.gregs[REG_ESP]; + registers[EBP] = thp->context.uap.uc_mcontext.gregs[REG_EBP]; + registers[ESI] = thp->context.uap.uc_mcontext.gregs[REG_ESI]; + registers[EDI] = thp->context.uap.uc_mcontext.gregs[REG_EDI]; + registers[PC] = thp->context.uap.uc_mcontext.gregs[REG_EIP]; + registers[PS] = thp->context.uap.uc_mcontext.gregs[REG_EFL]; + registers[CS] = thp->context.uap.uc_mcontext.gregs[REG_CS]; + registers[SS] = thp->context.uap.uc_mcontext.gregs[REG_SS]; + registers[DS] = thp->context.uap.uc_mcontext.gregs[REG_DS]; + registers[ES] = thp->context.uap.uc_mcontext.gregs[REG_ES]; + registers[FS] = thp->context.uap.uc_mcontext.gregs[REG_FS]; + registers[GS] = thp->context.uap.uc_mcontext.gregs[REG_GS]; } void stub_restore_regs(kz_thread *thp) { - thp->context.uap.uc_mcontext.mc_eax = registers[EAX]; - thp->context.uap.uc_mcontext.mc_ecx = registers[ECX]; - thp->context.uap.uc_mcontext.mc_edx = registers[EDX]; - thp->context.uap.uc_mcontext.mc_ebx = registers[EBX]; - thp->context.uap.uc_mcontext.mc_esp = registers[ESP]; - thp->context.uap.uc_mcontext.mc_ebp = registers[EBP]; - thp->context.uap.uc_mcontext.mc_esi = registers[ESI]; - thp->context.uap.uc_mcontext.mc_edi = registers[EDI]; - thp->context.uap.uc_mcontext.mc_eip = registers[PC]; - thp->context.uap.uc_mcontext.mc_eflags = registers[PS]; - thp->context.uap.uc_mcontext.mc_cs = registers[CS]; - thp->context.uap.uc_mcontext.mc_ss = registers[SS]; - thp->context.uap.uc_mcontext.mc_ds = registers[DS]; - thp->context.uap.uc_mcontext.mc_es = registers[ES]; - thp->context.uap.uc_mcontext.mc_fs = registers[FS]; - thp->context.uap.uc_mcontext.mc_gs = registers[GS]; + thp->context.uap.uc_mcontext.gregs[REG_EAX] = registers[EAX]; + thp->context.uap.uc_mcontext.gregs[REG_ECX] = registers[ECX]; + thp->context.uap.uc_mcontext.gregs[REG_EDX] = registers[EDX]; + thp->context.uap.uc_mcontext.gregs[REG_EBX] = registers[EBX]; + thp->context.uap.uc_mcontext.gregs[REG_ESP] = registers[ESP]; + thp->context.uap.uc_mcontext.gregs[REG_EBP] = registers[EBP]; + thp->context.uap.uc_mcontext.gregs[REG_ESI] = registers[ESI]; + thp->context.uap.uc_mcontext.gregs[REG_EDI] = registers[EDI]; + thp->context.uap.uc_mcontext.gregs[REG_EIP] = registers[PC]; + thp->context.uap.uc_mcontext.gregs[REG_EFL] = registers[PS]; + thp->context.uap.uc_mcontext.gregs[REG_CS] = registers[CS]; + thp->context.uap.uc_mcontext.gregs[REG_SS] = registers[SS]; + thp->context.uap.uc_mcontext.gregs[REG_DS] = registers[DS]; + thp->context.uap.uc_mcontext.gregs[REG_ES] = registers[ES]; + thp->context.uap.uc_mcontext.gregs[REG_FS] = registers[FS]; + thp->context.uap.uc_mcontext.gregs[REG_GS] = registers[GS]; } int stub_proc(kz_thread *thp, int signo)
diff -ruN kozos23.orig/t2w.c kozos23/t2w.c --- kozos23.orig/t2w.c 2007-12-04 22:43:50.000000000 +0900 +++ kozos23/t2w.c 2007-12-08 14:24:27.000000000 +0900 @@ -6,6 +6,15 @@ #includeあとはコンパイルを通すためとワーニング削るためのちょろちょろっとした 修正がいくつか入っている.#include +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; + +#define IS_ELF(ehdr) ( \ + ((ehdr).e_ident[EI_MAG0] == ELFMAG0) && \ + ((ehdr).e_ident[EI_MAG1] == ELFMAG1) && \ + ((ehdr).e_ident[EI_MAG2] == ELFMAG2) && \ + ((ehdr).e_ident[EI_MAG3] == ELFMAG3)) + int main(int argc, char *argv[]) { int fd, i;
で,以下が第23回のソースを Linux (Fedora Core 8)で動作させたときの スクリーンショット.
実行形式を起動,gdbで接続し,さらに telnet 接続して break コマンドを実行し, ブレークしたところ. ブレーク前までは時刻表示が行われていること, telnet が break コマンドを発行したところで停止していること, gdbでブレーク位置のソースが表示できていることに 注目してほしい.
ちなみに注意として, gdbでの接続,telnet での接続はどちらも LANインターフェースのアドレス(QEMUではDHCPで10.0.2.15が渡される)では うまくいかず,ループバックアドレス(127.0.0.1)でうまくいった.
うーん,Linux でもちゃんと動いたね.よかったよかった.