Monday, January 31, 2011

rebase for perlbrew Perl on Cygwin

Cygwinperlbrew 環境の Perl を使うときの、関連する DLL を rebase するシェルスクリプトです。

Cygwin には rebaseall や perlrebase がありますが、perlbrew でインストールした Perl の rebase までは面倒を見てくれません。brewedperlrebase (長い…)は perlbrew 環境用の perlrebase です。

どういうこと?

Cygwin は Windows 上に Linux 風の環境を提供するもので、様々な GNU ツールや UNIX 的な機能を利用できるようになります。Windows と UNIX 系 OS のプロセスモデルの最も大きな相違は自プロセスのコピーを生成する fork の有無です。
Cygwin では Windows 上にこの擬似的な fork() を実装するために DLL をロードするアドレスを調整する必要があります。

Cygwin の rebase は DLL のベースアドレスを変更するユーティリティで、プロセスにロードされる DLL のアドレスが重複せずそれぞれ一意となるように調整することで、fork() の親プロセスと子プロセスが同じメモリーイメージを持てるようにします。
基本的にロードされている DLL のアドレスさえ調整されていれば問題ないのですが、沢山ある DLL のうちどの DLL がどのプロセスでロードされるかは事前に知りようがないので、通常は rebaseall を使って Cygwin のほぼすべての DLL を rebase します。rebaseall するには対象となる DLL が使用されていない状態でなければいけないので、Cygwin のプログラムをすべて終了させておく必要があります。

Cygwin のパッケージでインストールされる DLL は既に調整された状態なので、通常の利用では rebase のお世話になることはありません。

しかし Perl のモジュールを CPAN からインストールしたとき、そこに XS を使ったモジュールが含まれていて DLL がインストールされた場合は別です。こういったモジュールを使った Perl プログラムはそのまま動くことが多いと思いますが fork() したときにはエラーが発生します。
例えば、

0 [main] perl 4704 C:\cygwin\bin\perl.exe: *** fatal error - unable to remap \\?\C:\cygwin\lib\perl5\5.10\i686-cygwin\auto\Fcntl\Fcntl.dll to same address as parent: 0x64700000 != 0x67290000
0 [main] perl 2520 fork: child 4704 - died waiting for dll loading, errno 11

のような。

これは rebaseall することで解決しますし、いちいち rebaseall するために Cygwin のプログラムをすべて終了させるのが面倒なときには、Cygwin の perl パッケージに含まれる perlrebase を利用することもできます。perlrebase は Perl に関連する DLL だけをまとめて rebase してくれるので、実行時に Perl を使ったプログラムさえ終了させておけば rebase できます。

perlrebase は /bin と /usr/local/bin にある Perl には対応していますが、perlbrew でインストールした Perl までは面倒を見てくれません(2回目)。ということで brewedperlrebase です。

CPAN からの芋づるインストールでは、特に依存関係にあるモジュールをロードしたあとのテスト時に fork() の問題が発生するので、逐次失敗させて rebase しながらインストールを進めるか、モジュールが動作すると分かっている場合にはテストを省略してインストールし最後にまとめて rebase します。

No comments:

Post a Comment