Linuxでも動きました!

2007/12/08

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

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 用に以下の修正を入れることで,無事動作することを確認 できました.

Linuxでコンパイルするための修正(その2)

上の修正は第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 
 #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)
さらに,テキスト領域を書き込み化にするための t2w に対して, Linux でコンパイルを通すための修正.t2w については第11回を参照.
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 でもちゃんと動いたね.よかったよかった.


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