目次へ戻る §13へ戻る §15へ進む

14. 小ネタ集

2005/7/22 - 2006/11/14 (鈴)

前回の予告どおり,今回は OpenGL によるちょっとした三次元フライトシミュレータの移植その他の小ネタ集です。

14.1 Cygwin の OpenGL

Cygwin には OpenGL が二つあります。 ひとつは Windows ネイティブのライブラリを利用するもの, もうひとつは X11 上に実装したものです。 前者のパッケージは opengl です。 GLUT (OpenGL Utility Toolkit) 以外, Windows のライブラリをそのまま使い,Cygwin の gcc から利用できるように補助的なファイルを用意します。 後者は実は X11 にすでに組み込まれており, GLUT だけ freeglut として別パッケージになっています。 どちらのパッケージも Graphics カテゴリにあります。 両者は共存できますから,両方インストールしても問題ありません。 後者を利用するには,プログラムを構築するとき, gcc に渡す -I オプション (ヘッダファイルのパスを指定) と -L オプション (ライブラリファイルのパスを指定) で /usr/X11R6/include と /usr/X11R6/lib を指定します。

前者は Windows ネイティブですから GPU のアクセラレーションを利用できます。 後者でアクセラレーションを利用するには, xorg-x11-xwin-gl パッケージによる実験的な X サーバを利用すればよいそうです (が実は筆者はまだ試していません)

skyfly - 空を飛ぼう

ここでは OpenGL の非常に印象的なデモプログラム skyfly を Windows ネイティブ版 OpenGL で動かしてみます。 このデモプログラムは, 公開されている GLUT のソース glut-3.7.tar.gz に同梱されています。 glut-3.7.tar.gz を ダウンロードして展開します。 glut-3.7/progs/demos/ 下の skyfly/ がそのソース一式です。

以下,使うのはディレクトリ skyfly/ だけですから, これだけ取り出して適当なところに置いてもかまいません。

コンパイルする前にまず Cygwin の opengl パッケージを忘れずにインストールしておきます。

skyfly/ 下のソース・ファイルに二つ手を加えます。 まず skyfly.h で関数 random を再定義している部分を消します。 それから random.c ファイルを消去します。 <stdlib.h> の標準の random() をそのまま使うわけです。 コンパイルは次の1行です。

01:~/skyfly$ gcc -O2 -o skyfly *.c -lglut32 -lglu32 -lopengl32

結果として得られる skyfly.exe を実行すると,画面モードが変わって, フルスクリーンで3次元のフライトシミュレーションが行われます (決して墜落も激突もしませんからリアリティには欠けますが…)。 マウスが操縦桿,マウスボタンが速度調整です。ESC キーの打鍵で終了します。

skyfly-slow - 空を飛ぼう,ゆっくりと

実行するとすぐに分りますが,skyfly は, アクセラレーションが効かない環境 (たとえば Virtual PC や Remote Desktop) では紙芝居のような動作になります。 一方,最近のマシンでは高速すぎてほとんど操縦不能になると思います。 速度は fly.c にハードコーディングされている数値で調整できます。 参考になるか分かりませんが, 手元のマシンでまあまあの加減になるように調整した fly.c を ソース一式と併せてまとめた skyfly-slow-200507.tar.bz2 を用意しました。 次のように使います (このとき, あらかじめ追加の Cygwin パッケージとして最低限 opengl と make と gcc-core を setup.exe で導入しておきます) *1

01:~$ bzcat skyfly-slow-200507.tar.bz2 | tar xf -
01:~$ cd skyfly-slow
01:~/skyfly-slow$ make
gcc -O2 -o skyfly *.c -lglut32 -lglu32 -lopengl32
01:~/skyfly-slow$ ./skyfly

14.2 共有ライブラリの表示

プログラム解析の第一歩は,プログラムがどの共有ライブラリ (といいますか,実体は Windows ですから DLL) に依存しているかを静的に把握することです。 Linux 等では「ldd ファイル名」を使いますが, Cygwin では「cygcheck プログラム名」を使います。 引数がファイル名ではなくプログラム名であることに注意してください。 下記の例のようにプログラム・ファイルがコマンド行と同様に検索されます。 接尾辞 .exe も省略できます。

01:~$ cygcheck vim
Found: C:\cygwin\bin\vim.exe
C:/cygwin/bin/vim.exe
  C:\cygwin\bin\cygwin1.dll
    C:\WINDOWS\system32\ADVAPI32.DLL
      C:\WINDOWS\system32\ntdll.dll
      C:\WINDOWS\system32\KERNEL32.dll
      C:\WINDOWS\system32\RPCRT4.dll
  C:\cygwin\bin\cygintl-2.dll
    C:\cygwin\bin\cygiconv-2.dll
  C:\cygwin\bin\cygncurses7.dll
01:~$ 

ただし,プログラム・ファイルがシンボリック・リンクになっている場合は うまく動かないようです。 (さらに下記で cygcheck viError を表示しているのにもかからわらず, プロンプトに表れているコマンド終了ステータスが 0, つまり成功という結果を示していることにも注意してください)

01:~$ cygcheck vi
Error: could not find vi
01:~$ type vi
vi is /usr/bin/vi
01:~$ ls -l /usr/bin/vi
lrwxrwxrwx  1 suzuki Users 7 Dec 31  2004 /usr/bin/vi -> vim.exe*

前節の skyfly.exe を cygcheck で調べてみるのも興味深いでしょう。 どれだけが Cygwin 由来で,どれだけが Windows 本来のライブラリか分かります。 なお,§2.4§3.1 で示したように,cygcheck にはパッケージ一覧などの機能もあります。

14.3 システムコールのトレース

動的にプログラムの振舞を追いかけるとき,gcc -g などとデバッグ情報付きでコンパイルし,gdb でソースの1行1行をステップ実行をすることもできますが, この方法ですべてまかなおうとすれば日が暮れて夜が明けても終わりません。 大まかな流れをカバーするには, 多くの場合,システムコールの呼び出し履歴だけで十分です。 つまり,カーネルの立場からプログラムの一挙手一投足を観察するわけです。 カーネルとの相互作用だけを見ますから, 個々のプログラムにデバッグ情報がなくても OK です。

Linux では「strace コマンド行」を使いますが, 実は Cygwin もそうです (Cygwin の場合, 厳密な意味でシステムコールと言ってよいかどうかは微妙ですが…)。 ただし,やはり Cygwin ではコマンド行のプログラム名にシンボリック・リンクが使えないようです。

strace の難点は, コマンド起動時の定型的な呼び出しが冒頭で大量に出力されることです。 普通これらは解析の手がかりになりません。 ひとつの方法としては,出力をいったんファイルにし, 問題のありそうな部分をテキストエディタの検索機能で見つけ, その前後で何をしているのかを調べます。 下記はプログラム本来の出力と混ぜてファイルにする例です。

01:~$ strace ls /usr/local > /tmp/poi
01:~$ meadow /tmp/poi

一方,こちらはプログラム本来の出力と分けてファイルにする例です。

01:~$ strace -o /tmp/poi ls /usr/local
bin  etc  lib  man  share
01:~$ meadow /tmp/poi

こちらのやりかたならば, スクリーン・エディタのような対話的プログラムも OK です。

01:~$ strace -o /tmp/poi vim

このとき同時にもうひとつ Cygwin のコマンド・プロンプトを開いて tail -f でモニタすれば, 対話的な操作に伴うシステムコール呼び出しを ほぼリアルタイムに追いかけることができます。

01:~$ tail -f /tmp/poi

14.4 フロッピーディスクへのバイナリイメージ書き込み

PC を使っていると,ときには OS の起動フロッピーディスクを バイナリイメージのファイルから作らなければならないことがあります。 特別なユーティリティを用意しなくても,Cygwin だけでフロッピーディスクへのバイナリイメージ書き込みが可能です。 §3.5 で少し説明したデバイス・ファイルを使います。

仮に ramf-20.img というバイナリイメージのファイル*2があったとして, A: ドライブ (USB 接続でも可) のフロッピーディスクに書き込むには

01:~$ dd if=ramf-20.img of=/dev/fd0

とします。ただし,これはいささか低速です。 仮にイメージファイルが 1474560 バイトの大きさならば

01:~$ dd if=ramf-20.img of=/dev/fd0 bs=1474560

とすると,心もち速くなります。

14.5 ファイルのアクセス制御

Windows 2000/XP で使われる NTFS のアクセス制御は Unix の3層モデルよりも柔軟です。 ls -l としたとき, ファイルパーミッションの末尾に + が付いているのは, Unix のやりかたでは表現できないアクセス制御が指定されていることを示します。 getfacl コマンドを使えば,それを表示できます。 setfacl コマンドを使えば変更できます。

NTFS のアクセス制御による奇妙な現象として,

01:~$ notepad poi.txt 

のようにホームディレクトリで非 Cygwin のプログラム (この例では notepad, つまり「メモ帳」) を使ってファイルを作ると, そのパーミッションが -rwxrwxrwx になる,ということがあります。

もしも Cygwin をインストールしたばかりならば, group と other に write パーミッションが付かないようにするには, Cygwin でディレクトリを作り直してしまうのが簡単です。

01:~$ cd ..
01:/home$ mv suzuki suzuki~
01:/home$ mkdir suzuki

getfacl コマンドで確かめてみます。

01:/home$ ls -d -l *
drwxr-xr-x+ 3 suzuki None 0 Jul 12 02:25 suzuki/
drwxr-xr-x+ 2 suzuki None 0 Jul 12 01:35 suzuki~/
01:/home$ getfacl *
# file: suzuki
# owner: suzuki
# group: None
user::rwx
group::r-x
mask:rwx
other:r-x
default:user::rwx
default:group::r-x
default:other:r-x

# file: suzuki~
# owner: suzuki
# group: None
user::rwx
group::r-x
mask:rwx
other:r-x
default:user::rwx
default:group::rwx
default:other:rwx
01:/home$

これからわかるように,この現象はデフォルト・エントリの設定によるものですから, 次のように setfacl を使っても修正できます。

01:~$ setfacl -m default:group::r-x .
01:~$ setfacl -m default:other:r-x .

14.6 Cygwin の小 bug

Cygwin-Doc メニュー

はじめて Cygwin をインストールしたとき, Windows の「スタート」メニューの Cygwin のエントリに ドキュメントのショートカットが作られないことがあります。

その場合は setup.exe を再び起動して,cygwin-doc パッケージを reinstall します。

cygwin-doc パッケージは少し前から base カテゴリに追加され, はじめて Cygwin をインストールしたときインストールされるようになりました。 しかし,インストール時の処理を行うときはまだ「スタート」メニューに Cygwin の項目がないため,そのままショートカットが作られないままになって しまうわけです。詳しくはインストール時の処理をしたスクリプト (といいますか, 正確にはその形見) である /etc/postinstall/cygwin-doc.sh.done をご覧ください。

Bash 3.0 のプロンプト

2006.11/14 追記: ここで述べていたバグは 2006年 11月11日 (日本時間) ごろ配布が開始された bash 3.2.3 + readline 5.2 で 1 年 4 ヶ月ぶりに修正されました。 ~/.bashrc で環境変数 TERM を細工する必要はなくなりました。下記のような回避策をとっていた人はようやく元に戻すことができます。

この七月,Cygwin の bash が 3.0 にメジャー・バージョンアップしました。 しかし,このバージョンには, 凝ったプロンプトにすると補完操作後の表示がおかしくなるという, 既知のバグ*3があります。 残念ながら §5.1 で導入したプロンプトもこのバグの犠牲者になっています。たとえば

01:~$ ls .bas
.bash_history   .bash_profile~  .bashrc~~
.bash_profile   .bashrc
01:~$ ls .basas

さしあたりの回避策のひとつは,環境変数 TERM の値を "cygwin" でなく "cygwinB19" にすることです。 具体的には ~/.bashrc に次の行を入れます。

[ $TERM = cygwin ] && TERM=cygwinB19

ただし,効率のよいエスケープ列を使わなくなるため, 画面表示がややトロくなるなどの副作用があります。 とりわけ man コマンドへの副作用が目に付きます。

01:~$ man ls
WARNING: terminal is not fully functional
-  (press RETURN)

man コマンドが内部利用している less コマンドに対策を施します。 §5.1 で "MrXE" と設定したLESS 環境変数の値に次のように d を追加します。 d オプションは次のような副作用による警告表示を抑制します。

export LESS=MrXEd

次回予告

本来ならば §6 となるはずが ThinkPad X31 の事故で失われて一年あまり, 次回は (気をとり直して) Python について書きます。


脚注

*1 筆者が skyfly を最初に知ったのは, 2001 年の暮れか,2002 年の正月ごろ Apple の Mac OS X 開発者ツールの GLUTExamples に含まれていたのを iBook G3 12"/600MHz でコンパイルし,走らせたときでした。 当時はオリジナルのパラメータでちょうどよい速度でした。 しかし, 同じプログラムを約1年後に PowerBook G4 15"/1GHz で走らせたときは, もう手に負えないぐらい動作が高速になっていました。 新しい版の開発者ツールからいつのまにか skyfly が消えていましたが, むべなるかなと思ったものでした。

今回の「ゆっくり」版 skyfly のパラメータは, 実は昨年春の PowerBook G4 15"/1.5GHz に合わせたものです。 その後,そのソースを Cygwin にもってきたところ, ヘッダファイルのパスの違い等が少しあっただけで, あっさりコンパイルできたばかりでなく, いくつかのノート PC (たとえば Intel 855 チップセット内蔵グラフィックス & Pentium M 1.7GHz, 同 852 & Mobile Celeron 2.0GHz, Trident XP4 & Pentium M 1.0GHz など) でまあまあ妥当な速度で動いたのは, うれしい驚きでした (Intel 915 & Pentium 4 3.2GHz のデスクトップ PC で動作が緩慢だったのは意外でしたが)。 それを今回,今では入手困難な Mac OS X 2001 年 12 月版開発者ツールではなく, 誰でもすぐ手に入るかたちで配布されている原典版を探し出して, そのソースをもとにこうして記事にしたわけです。

ただし,この「ゆっくり」版の速度調整は完全ではありません。 速めのマシンで動かすと特にそう感じますが, 一緒に飛んでいる折り紙飛行機の動きがどうも今ひとつ飛行機らしくない, といいますか,まるで魚が泳いでいるような一種生々しい感じがします (「あれは絶対,何か考えている」と感想をくれた人もいました。 もちろん,中はただの乱数です)。 今回は偶然そうなったわけですが,人間がそう感じる理由を追求すると, 興味深い新発見ないし再発見があるかもしれません。

ちなみに Mac OS X でとりあえず動かすときは次のようにします (Mac OS X 10.4.2 で確認しました。 正攻法としては Xcode プロジェクトにして SkyFly.app に仕立てるべきでしょう)。 ln コマンドと -I. オプションは, ヘッダファイルのパスの違いを吸収するための細工です。 今回使っているのは開発者ツール由来のソースではありませんから, Cygwin へ移植した時とは逆に Mac OS X のほうで修正する必要があるわけです。

01:~/skyfly-slow$ ln -s /System/Library/Frameworks/GLUT.framwork/Headers GL
01:~/skyfly-slow$ gcc -O2 -o skyfly *.c -I. -framework GLUT -framework OpenGL

*2 実は ramf-20 は http://www.ibiblio.org/pub/Linux/system/recovery/ で配布されている緊急復旧用 Linux です。 ときどき筆者は,光学ドライブのないノート PC 等で, これを USB FDD から起動し, 内蔵ハードディスクのパーティションの確認や変更などをしています。

*3 たとえば http://cygwin.com/ml/cygwin/2005-07/msg00431.html を参照してください。 残念ながら,そこに書かれている回避策は, ここで使っているプロンプトには効果がないようです。

目次へ戻る §13へ戻る §15へ進む

Copyright (c) 2005 Oki Software Co., Ltd.