ようこそゲストさん

adiary開発日誌

お知らせ

絶対使わないと言い切れますか? blog + wiki = adiary

2010/02/22(月) SpeedyCGI で ImageMagick が動かない

Webアプリ開発中に ImageMagick(perlmagick)が SpeedyCGI 環境でまともに動かない不具合に当たりました。

問題の詳細

  • Image::Magick をロードして何かしら処理を行うルーチンを SpeedyCGI で実行する時、2回目以降のキャッシュされた状態で不具合が起こる*1
  • 問題確認環境
    • Perl 5.8.8 + SpeedyCGI 2.22
    • ImageMagick 6.3.7 および 6.5.9

この問題は FastCGI や mod_perl2 では起きません。

*1 : スクリプトの処理が停止してしまう。もしくは処理中のまま返ってこなくなる

問題の症状

スクリプト

#!/usr/bin/speedy

use Image::Magick();

print "Content-Type: text/plain\n\n";
print "Image::Magick Version $Image::Magick::VERSION \n";
my $image = Image::Magick->new;
$image->Read( 'x.png' );
}

実行結果。

~$ test.pl
Image::Magick Version 6.3.7
~$ test.pl
Segmentation fault
~$ test.pl
Image::Magick Version 6.5.9
~$ test.pl
Segmentation fault

問題の原因

不明。

問題の解決法??

ENDブロックを呼ばれないようにして終了時のメモリ開放はOSやPerl自身に任せる。

#!/usr/bin/speedy

use Image::Magick();
sub Image::Magick::END {}

print "Content-Type: text/plain\n\n";
print "Image::Magick Version $Image::Magick::VERSION \n";
my $image = Image::Magick->new;
$image->Read( 'x.png' );
}

たしかにこれで問題は起こらなくなるのですが、

$image->Write( 'x.jpg' );

をするだけでダメになる。とにかくメモリリークっぽい挙動ですが、詳細が不明の挙動不明で、試行錯誤の末 Image::Magick をロードするダミーモジュールを作成しましたが、これで回避できるケースと回避できないケースがあり暗中模索。

  • ImageMagick.pm
    use strict;
    package ImageMagick;
    sub new {
    	require Image::Magick;
    	return Image::Magick->new;
    }
    
  • main.pl
    use ImageMagick();
    my $img = ImageMagick->new;
    #
    # $img は Image::Magick のオブジェクトであるので、
    # Image::Magick と同様に利用できる。
    #
    

まとめ

とりあえず現在のところ挙動が意味不明です。ファイル入出力と関連してバグっている様ですが……。

gccの(ライブラリlibgomp )バグのようです。gcc 4.3より前のVersion(4.2以下)で、amd64環境の時起こる模様。

メモ

256色png(8bitカラーパレット)で保存する方法。

$image->Read( 'x.png' );
$image->Resize(width=>133, height=>100);
$image->Quantize(colorspace=>'RGB',colors=>256);
$image->Set(depth => 8); 
$image->Write( 'y.png' );

1: 2010年03月13日(土) 午後10時55分

ものすごく勘でものを言いますが。
PerlでSQLiteのオブジェクトが解放されない現象に似ているかもしれません。
使い終わった Image:Magick オブジェクトを明示的に delete $image; してみるとどうでしょう?

2: なべ 2010年03月13日(土) 午後11時43分

delete $image; はできないので、undef $image したけど同じですね。
ENDが内部的に呼ばれてるので、オブジェクトの破棄はされてるんですよね。


名前:   

  • TB-URL  http://adiary.blog.abk.nu/0267/tb/
  • ApacheのRLimitMEMが効かない adiary開発日誌 なべ
    CGI開発中についうっかり無限ループを生成するなんて経験のある人も多いと思いますが、つい最近2回連続でサーバごと落としてしまったので*1本格的に対策しました。 *1 : メモリオーバーフローでカーネルが端からプロセスを殺し始めてサーバ停止■RLimitME...