0

私はセグメンテーション違反を行っていたスクリプトを持っていましたが、それを解決する方法にあまり満足していないので、ここに質問を投稿して、その原因についてもう少し理解し、より良い解決策を見つけたいと思いました.

これが私のスクリプトの動作です(「コア」を残すために詳細なコードをいくつか削除しました):

# Here's a query I need to do every X seconds to monitor progress of other tasks
# This is apparently the key to my segfault problem
my $stmt = $dbh->prepare($query);

my $all_done = 0;
while(!$all_done) {
    $self->debug("Waiting for $n blocker tasks to be finished");

    # Execute query to pull the status of the tasks from DB
    $stmt->execute();

    my $pending = [];
    while(my $hr = $stmt->fetchrow_hashref()) { 
        push @{$pending}, $hr->{'TASK_NAME'} if ($hr->{'STATUS'} ne 'COMPLETE'); 
    }

    if(scalar(@{$pending}) > 0) {
        $all_done = 0;
        sleep($sleep_gap);
    }
    else { $all_done = 1; }
}

これで、ほとんどの場合、スクリプトはうまく機能します。ただし、スクリプトの 3 つ以上のインスタンスが並行して実行されている場合(スレッドではなく、同じスクリプト、別のプロセス)、segfault になります。

どのように解決しましたか?while(!$all_done) ループ中に毎回 DBH::prepare 呼び出しを実行して解決しました。

したがって、このコードは、複数のプロセスが並行して実行されていても、セグメンテーション違反を起こしません。エラーを一貫して数回再現し、新しいコードで同じことを行いました。ステートメントをループ内に移動すると、問題が修正されると確信しています。

なぜこれが起こっているのでしょうか?

perl 5.8 と perl-DBI バージョン 1.609 を使用しています。

スクリプトがセグメンテーション違反を起こしたときの strace の出力も次のとおりです。

read(5, "\1\7\0\0\6\0\0\0\0\0\20\27\234\312\272\221eG2;\33S\364\230\313\220\221Bxp\4\7"..., 2064) = 263
write(4, "\1\31\0\0\6\0\0\0\0\0\21i \1\1\0\0\0\2\0\0\0\3^!)\4\4\0\0\0\0"..., 281) = 281
read(4, "\0\177\0\0\6\0\0\0\0\0\v\5\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0  \10\6"..., 2064) = 127
write(2, "debug:Waiting for 1 blocker task"..., 49debug:Waiting for 1 blocker tasks to be finished
) = 49
write(5, "\0\252\0\0\6\0\0\0\0\0\3^\20p\200\0\0\2\0\0\0\0\0\0\0\0\1\r\0\0\0\0"..., 170) = 170
read(5, "\0\301\0\0\6\0\0\0\0\0\6\"\2\0\0\0@\0\0\0\0\0\0\0\0\0\0\0\7 ru"..., 2064) = 193
write(5, "\1]\0\0\6\0\0\0\0\0\3^\21)\200\0\0\0\0\0\0\1\234\0\0\0\1\r\0\0\0\0"..., 349) = 349
read(5, "\0y\0\0\6\0\0\0\0\0\10\6\0S\254b\f\0\t\0\0\1\0\0\0\1\0\0\0\0\0\0"..., 2064) = 121
write(5, "\0000\0\0\6\0\0\0\0\0\3h\22\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 48) = 48
read(5, "\0\26\0\0\6\0\0\0\0\0\10\2\0\0\0\t\5\0\0\0\21\0", 2064) = 22
time(NULL)                              = 1333827285
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({10, 0}, {10, 0})             = 0
time(NULL)                              = 1333827295
write(4, "\1\31\0\0\6\0\0\0\0\0\21i\"\1\1\0\0\0\3\0\0\0\3^#)\4\4\0\0\0\0"..., 281) = 281
read(4, "\0\177\0\0\6\0\0\0\0\0\v\5\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0  \10\6"..., 2064) = 127
write(2, "debug:Waiting for 1 blocker task"..., 49debug:Waiting for 1 blocker tasks to be finished
) = 49
write(5, "\0)\0\0\6\0\0\0\0\0\21i\23\1\1\0\0\0\1\0\0\0\3N\24\2\0\0\0@\0\0"..., 41) = 41
read(5, "\1>\0\0\6\0\0\0\0\0\20\27\234\312\272\221eG2;\33S\364\230\313\220\221Bxp\4\7"..., 2064) = 318
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
+++ killed by SIGSEGV +++
[ Process PID=22767 runs in 32 bit mode. ]
4

1 に答える 1

1

SQLite ステートメントの準備は高速になる傾向があるため、あまり心配する必要はないと思います。私は、Perl DBI による単純な SQL の準備が 20 マイクロ秒しかかからないことを測定しました (以下の結果とコードを参照)。アプリケーションを考慮すると、パフォーマンスの違いに気付かないはずです。

SQLite の従来のロックでは、一度に 1 つのライターと複数のリーダーしか許可されないため、直面している問題はロックまたはトランザクション処理に関連している可能性があります。ただし、SIGSEGV は予期される動作ではありません。

結果

Perl 5.014002
DBI 1.618
SQLite 3.7.9
time in s for 100000 prepares: 2.01810789108276
ys per prepare: 20.1810789108276

コード

use DBI;
use Time::HiRes qw ( time );
use strict;
use warnings;

my $dbc = DBI->connect (
 'dbi:SQLite:dbname=/tmp/test.db',
  undef, undef, { AutoCommit => 0, RaiseError => 1, PrintError => 1 }
) || die $DBI::errstr;

print "Perl $]\n";
print "DBI $DBI::VERSION\n";
print "SQLite $dbc->{sqlite_version}\n";

my $start = time();

my $n = 100_000;

foreach ( 1 .. $n ) {

 my $stmt = $dbc->prepare( qq{
  select count(*) from sec where sid is not null
 } );

}

my $end = time();

print
 "time in s for $n prepares: " .
 ( $end - $start ) .
 "\n";

print
 "ys per prepare: " .
 ( ( ( $end - $start ) * 1_000_000 ) / $n ) .
 "\n";
于 2012-04-07T21:44:46.627 に答える