2012-04-07 7 views
0

Ich hatte ein Skript, das segfault und ich bin nicht wirklich bequem, wie ich es gelöst habe, also wollte ich die Frage hier zu stellen, um ein wenig mehr über die Ursache davon zu verstehen, und vielleicht eine bessere Lösung.Verständnis von Segfault mit Perl-DBI

Hier ist, was mein Skript macht (entfernt einige detaillierte Code, um den „Kern“, es zu verlassen):

# 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; } 
} 

Nun, das Skript funktioniert gut, die meiste Zeit. Es wird jedoch auf segfault umgestellt, wenn 3 oder mehr Instanzen des Skripts parallel ausgeführt werden () (dasselbe Skript, separate Prozesse, keine Threads).

Wie habe ich es gelöst? Ich löste es, indem ich den DBH :: Prepare-Aufruf jedes Mal während der while (! $ All_done) Schleife machte.

Also, dieser Code segmentiert nicht, auch wenn mehrere Prozesse parallel laufen. Ich reproduzierte den Fehler mehrere Male konsistent, und dann tat ich dasselbe mit dem neuen Code. Ich bin sicher, dass das Verschieben der Anweisung innerhalb der Schleife das Problem BEFESTIGTE.

Irgendwelche Ideen, warum das passieren kann?

Ich benutze Perl 5.8 und Perl-DBI Version 1.609.

Hier ist auch die Ausgabe von strace, wenn das Skript segfaults:

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\[email protected]\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\[email protected]\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. ] 
+0

Nur um meinen Beitrag hinzuzufügen, falls es nicht klar genug war: Handeln "prepare()" nur einmal und dann mehrere „execute () "Anrufe sollten der richtige Weg sein, aber so bekomme ich Segfaults. Auf der anderen Seite, indem ich prepare() und execute() die ganze Zeit mache, vermeide ich den segfault. – juansaba

+0

Wird der DBH in dem Thread erstellt, in dem sie verwendet werden? – ikegami

+0

Wenn Sie sagen "mehrere Instanzen" hat das Skript 'fork' oder wurden die Instanzen unabhängig gestartet? – cjm

Antwort

1

Ich würde sagen, Sie haben nicht viel zu kümmern, da SQLite-Anweisung vorzubereiten neigt, schnell zu sein. Ich habe gemerkt, dass eine einfache SQL-Vorbereitung über Perl DBI nur 20 Mikrosekunden dauerte (siehe Ergebnisse und Code unten). In Anbetracht Ihrer Anwendung sollten Sie keinen Unterschied in der Leistung bemerken.

Da das klassische Locking von SQLite nur einen Writer und mehrere Reader gleichzeitig erlaubt, kann das Problem, mit dem Sie konfrontiert sind, mit dem Sperren oder der Transaktionsverarbeitung zusammenhängen. SIGSEGV ist jedoch nie der erwartete Behavior.

Ergebnisse

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

-Code

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"; 
+0

Warum denken Sie, dass die zugrunde liegende Datenbank SQLite ist? – mob

+0

Nun, da ich die Frage erneut gelesen habe, gibt sie nicht wirklich an, was die db ist. Ich nehme an, dass es eine Vermutung gewesen sein könnte oder dass ich gerade SQLite-bezogene Fragen gerade vor der Beantwortung durchstöbert habe. – XDF