ホーム > 読んだ > 国内

Inside Cygwin
インサイド シグウィン

書誌

tagUNIX
text唯野
author藤枝和宏
publisherアスキー
year『UNIX MAGAZINE 2000.12-2001.6』

目次

1感想
2抄録

履歴

2000-2001読了
2001.3.15公開
2002.10.31修正
2012.1.17タグ追加

感想

ユニマガでやっている Cygwin の連載記事をまとめたもの。(記事は現在も連載中なので、適時、内容は更新する予定。)もちろん、私自身にとって意味のある部分だけを抜き出しているので、内容的には偏りがあるが、類似の記事がないだけに、大変参考になっている。

抄録

1 Cygwin のインストール (2000.12/p.74-81)

仕様だけでなく、なぜそういう仕様になっているのかの説明まで。

インストールまで

Cygwin は Beta 20.1 まで単体の exe ファイルひとつで配布されていたが、ダウンロード時のファイル破損などを避けるため、バージョン 1.1 よりパッケージごとの tar.gz で配布されるようになった。パッケージは特にバージョン管理されておらず、国内では主に Ring Server などで FTP 配布されている。latest ディレクトリに Cygwin 開発チームがサポートするパッケージ、contrib ディレクトリにサポートのされないパッケージが格納されている。

パッケージの tar.gz に対してはシンボリックリンクを処理するために Cygwin の tar を使う必要がある。そのための gzip、tar、cygwin1.dll をまとめた bootstrap.zip もあるが、現在ではインストーラ(setup.exe)を使えばよい。そして、インストール時には NT/2000 であれば Administrator 権限で行い、ウイルスチェッカの検索を止めるなどしておくようにする。但し、ファイルのダウンロードをインストール時に行う場合、ダウンロードの失敗に対するレジューム機能はないので、事前にファイルを FTP でダウンロードしてから Install From Local Directory を選択する方がよい。また、インストール先ディレクトリが空白を含むことなどは避けるようにする。Default Text File Type はバイナリ(Unix)で特に問題ない。

インストーラはパッケージ間の依存関係を知らないので、特に cygwin パッケージを外してはならない。一方、画像ファイルの扱うプログラムをコンパイルしないのであれば jbigkit、jpeg、libpng、tiff は不要であり、そもそもアプリケーションのコンパイルを行わないのであれば binutils、bison、byacc、dejagnu、expect、flex、gcc、gdb、gdbm、gperf、tcltk なども不要となる。それ以外で注目すべき依存関係としては gcc -> binutils、dejagnu -> expect、expect -> tcltk、inetutils -> login、tiff -> zlib/jpeg などが挙げられる。

インストール後

インストール後には環境変数 HOME にホームディレクトリを、パスの通ったディレクトリに Cygwin の実行パスを追加する。デフォルトでは bash 起動時の /etc/profile でこれらが設定される。このとき、パスの追加では Cygwin のパスが先頭へ来るようにする。(そうでないと find などで Windows 版が使われてしまう。)

Windows NT/2000 ではデフォルトで /etc/passwd、/etc/group が作成されるが、Win9x/Me でも inetd や sshd の利用では必要となるので作成した方がよい。/etc/passwd の作成は bash から

$ echo "$USER::500:544::$HOME:$SHELL" > /etc/passwd

した後、crypt コマンドに引数にパスワードを指定して実行した戻り値を、作成された /etc/passwd の最初の :: の間に挿入する。また、NT/2000 上では mkpasswd、mkgroup で /etc/passwd、/etc/group を作成できるが、これららそのままだとローカル・コンピュータのユーザしか処理しないため、ドメイン管理されているユーザが Administrator 扱い(user ID が 500)になってしまう。その場合には、ドメイン情報を引けるユーザで c:\cygwin から以下のコマンドを実行する。ちなみに、これらの変更の反映はいったん Cygwin を全て終了しないと有効にならない。

bin\mkpasswd -l -d > etc\passwd
bin\mkgroup -l -d > etc\group

Cygwin 上からはインストール先にかかわらず、インストール先ディレクトリが / となる。このとき Windows 側から見ると空になっている /usr/bin と /usr/lib は C:\bin と C:\lib をマウントしている。その際、/ と \ の相違は Cygwin のマウント機能によって解決される。但し、癖があるので、詳細は次回を参照のこと。

2 マウント機構の詳細 (2001.1/p.95-100)

Cygwin はインストーラの設定したマウントテーブルに従い、インストール・ディレクトリをルート・ディレクトリにする。具体的には下記の通り。

$ mount
Device         Directory  Type    Flags
C:\cygwin\bin  /usr/bin   system  binmode
C:\cygwin\lib  /usr/lib   system  binmode
C:\cygwin      /          system  binmode

このためデフォルトでは C:\Cygwin 以下は参照できない道理になるが、普通に Windows 側のディレクトリを参照することもできる。(但し \ の利用はトラブルになりやすい。) 1.1 では /cygdrive/c/windows などという参照が可能になった。単に cd d: などとするだけで任意のドライブのルート(/cygdrive/[a-z]/)へ移動することができる。

ここで Cygwin の mount コマンドの簡単な使い方と主なオプションを以下に示す。

$ mount -b D:/temp /tmp など -b バイナリモード -t テキストモード (デフォルト) -x 全ファイルを実行ファイルとして扱う -s システムのマウントテーブルに書き込み -u ユーザのマウントテーブルに書き込み (デフォルト) -f 既存のマウントエントリを上書き $ umount /tmp -s システムのマウントテーブルを対象にする

Cygwin のシステムコールやライブラリ関数は、ファイルを(最も近いディレクトリの)マウントフラグによってテキスト/バイナリに判断する。基本的にはバイナリモードを指定するようにしておけばよい。また、Cygwin では実行権を確認するのに(FAT には実行権の概念がないので)ファイル先頭の #! などを調べているためコストがかかる。そこでディレクトリ単位に実行権を与える目的で -x を使う。ほかに、-s/-u でマウントエントリを指定する。エントリはレジストリに書き込まれるが、最初に起動したアプリが内容をキャッシュして使い回せるようになっている。

Cygwin DLL のバージョンが 1.1.5 以降なのであれば、mount で以下のオプションを利用できる。

// 「cygdrive」部分を「パス名」に置き換える
mount [-sb] --change-cygdrive-prefix パス名
// ドライブのマウントポイントを表示
mount --show-cygdrive-prefix

3 バイナリとテキストの区別のある世界 (2001.2/p.137-141)

アプリケーション側でテキスト/バイナリのフラグを指定している場合、(マウントフラグが無視される結果)期待通りに動かない場合がある。また、patch のようにアプリケーションがモードを指定していなくても、マウントフラグの無視するプログラムも存在する。

これらは、基本的には利用する全てのディレクトリをバイナリモードでマウントしておくことで大抵のトラブルは回避できる。ディスク上のファイルのモードは「アプリケーションによるフラグ指定 > 最も近い親ディレクトリのフラグ > ドライブのマウントフラグ」の順に決定されるが、これは POSIX 形式のパス名でしか適用されない。それゆえ「ドライブ名:」というパス名の場合には、マウント元の Windows のパス名から見て最も近い親ディレクトリのマウントフラグが参照される。その際、どこにもマウントされていないディレクトリはテキストモードが指定されるので、これを避けるには明示的にドライブをマウントしておけばよい。

$ mount -b -f C: /c

同様に UNC (Universal Naming Convention : \\ホスト名\共有名) のパス名もデフォルトではテキストモードとなるので、明示的に「共有名」を含んだパス名をバイナリモードでマウントする。

$ mount -b //winfs/share /winfs

一方、ディスク上のものではないファイルのモードは以下の優先度に従って決定される。「アプリケーションによるフラグ指定 > Windows のコンソールデバイスならテキストモード > 環境変数ならテキストモード > その他はバイナリモード」。このとき inetd などで外からログインした場合は、コンソールデバイスではないのでバイナリモードとなる。

逆にあえてテキストモードを利用したい場合には、アプリケーションによる指定が行われない sed や awk を使う。こうすればマウントフラグに従った意図通りの制御ができる。具体的には「sed -n p」などの何もしないフィルタを通してテキストモードのディレクトリへリダイレクトすれば、LF が CR+LF になる。

$ sed -n p hoge > /text/hoge.cr

そして、アプリケーション内でモードを指定するには fopen( ) や open( ) で必要なモード指定をすればよい。既にオープンされているファイルの場合も setmode( ) で変更できる。これをアプリケーション全体に対して指定するには、グローバル変数 _fmode に O_BINARY か O_TEXT を指定すればよい。(これは標準入出力など既に開いているものやパイプには反映されない。)アプリケーションを変更せずにこの指定を行うには、binmode.o か textmode.o をリンクすることで可能となる。更に Cygwin DLL 1.1.5 では automode.o という読み込みでテキストモード、書き込みでバイナリモードとなるオブジェクトファイルも用意されている。

4 Windows のアプリケーションとの共存 (2001.3/p.121-125)

Cygwin のマウント機構を用いると Cygwin 上と Windows 上ではパスの解釈が異なるため、同じファイル名として一致しない。そこで Cygwin 上でのパス名を Windows 上でのそれに変換する cygpath コマンドが用意されている。(もちろん逆もできる。) この cygpath コマンドを使って起動用スクリプトを用意し対処する。例えば Java や #! で始まるスクリプトの起動などに応用できる。

cygpath [option] path

-a  --absolute          絶対パスの出力
-c  --close <数字>      数字のハンドルをクローズ
-f  --file <ファイル名> ファイルからパス名を読み込む
                        (ファイル名が - なら標準入力から読み込む)
                        改行による複数指定も可能
-u  --unix              Cygwin のパス名に変換
-w  --windows           Windows のパス名に変換
-s  --short-name        短い名前を出力
-W  --windir            Windows ディレクトリの出力
-S  --sysdir            system ディレクトリの出力
-p  --path              パス名のリスト
                        (Cygwin の PATH の : を ; に変換)
-i  --ignore            引数の指定の誤りを無視

start コマンドはコマンドラインから Windows アプリケーションを立ち上げることができるが、引数として URL を指定するとブラウザを立ち上げ、指定したコンテンツを表示することもできる。しかし、逆に start コマンドは Cygwin のパス名を解さないので、この場合も変換が必要となる。

// start コマンドのためのシェルスクリプト (内容は丸写し)

#! /bin/sh

if [ -f "$1" ]
then
    arg='cygpath -aw "$1"'
else
    arg="$1"
fi

case 'uname' in
*NT*)
    case "$CYGWIN" in
    *ntsec*)
        if [ -f "$1" ]
        then
            chmod +x "$1"
        fi
        ;;
    esac
    cmd /c start "$arg" > /dev/null 2>&1
    ;;
*)
    'cygpath -W '/cmd/start start "\"$arg\""
    ;;
esac

スクリプトの解説をすると、引数が URL であってもよいように最初にファイルかどうかチェックする。そして、start は Win 9x/Me では独立したコマンドだが、Win NT/2000 ではコマンドインタプリタ(cmd)の組み込みコマンドなので、uname で OS を調べて分岐している。また、NT/2000 では実行権限が必要なので chmod で実行権を付与し UNC パス名も通るよう絶対パスを取得している。

# ちなみに、この回では Meadow と Cygwin のケースも扱っているが、
# こちらは個人的にあまり関心のないトピックなので割愛する

5 Cygwin のベンチマーク (2001.4/p.129-136)

Cygwin の遅さについて。特に 9x/Me ではコンソール I/O の Win32 API の遅さに引きずられて非常に処理が遅い。そうでなくても Cygwin DLL では select に渡せるファイル記述子の最大値が 64 であったり(sys/types.h のインクルード前に FD_SETSIZE で変更可能)、子プロセスが最大 63 という制限(これは WaitForMultipleObject という API が同時に持てるオブジェクトの数)などを負っている。(WaitForMultipleObject はコンソール、スレッド、セマフォなどの状態変化を待つが、パイプなら PeekNamePipe、ソケットでは Winsock の select などと使い分ける。)

Cygwin は VMware 上の NetBSD よりも更に遅いので、ベンチマークとしては「Cygwin がいかに遅いか」を調べるのがメインになっている。特に顕著なのは stat、open/close、ファイルの select、TCP の select、シグナル、fork/exec など。とはいえ、逆にいうとシステムコールを使っていない処理であれば、速度は PC 上の UNIX と同程度になる。

# 今回は Cygwin のベンチマーク環境の構築と結果がメインになっている
# いずれも関心のないトピックスなので詳細は省いた

全文を読まれる場合はログインしてください


Up