2014-05-05 44 views
2

當談到Perl如何處理POSIX信號時,我遇到了一些問題。也就是說,Perl似乎忽略了這些信號,除非在sleep()的調用中收到它們。Perl 5.10 - POSIX信號被忽略,除非在sleep()調用期間收到?

例如,下面的代碼工作正常:

#/usr/bin/perl 
$SIG{PIPE} = sub { print STDERR "WARNING: Received SIGPIPE"; exit(1); }; 
while (1) { print "Waiting on signal...\n"; sleep(10); } 

當使用其他腳本從Oracle數據庫讀取上面的SIGPIPE處理程序,子程序似乎永遠不會被調用。

#/usr/bin/perl 
use DBI; 

$SIG{PIPE} = sub { print STDERR "WARNING: Received SIGPIPE"; exit(1); }; 

my $db = "redacted"; 
my $user = "redacted"; 
my $pass = "redacted"; 
my $table = "redacted"; 

my $ora = DBI->connect("dbi:Oracle:" . $db, $user, $pass); 
my $sql = "SELECT * FROM " . $table; 
my $query = $ora->prepare($sql); 
$query->execute(); 

while (my @row = $query->fetchrow_array()) { 
    print(join('|', @row) . "\n"); 
} 

if ($DBI::err) { print STDERR "ERROR: Unload terminated due to error"; } 

我發送SIGPIPE信號,以同樣的方式(kill -sPIPE pid)這兩個腳本,但只有第一個腳本對其作出響應。第二個腳本只是繼續。沒有消息,沒有退出,什麼都沒有。

我該如何糾正這種情況?

+0

無法重現。 Oracle驅動程序是否可能也試圖使用'$ SIG {PIPE}'?在執行調用之前和之後檢查'$ SIG {PIPE}'的值。 – mob

+0

我在執行調用之前/之後得到相同的代碼散列。爲了確保設置值,我還在設置處理程序之前打印了值(它已按預期更新)。爲了使問題複雜化,在處理循環之前添加一個「sleep(30)」可以使其捕獲信號,但只能在睡眠期間進行。 –

+0

讀取不生成SIGPIPE;當您嘗試寫入沒有接收器進程的管道或套接字時,您會收到SIGPIPE。 –

回答

1

在DBI調用之前設置信號處理程序導致它在調用某些DBI方法後被忽略。解決的辦法是在信號處理子程序移動到之前的處理循環,但通話結束後執行:

#/usr/bin/perl 
use DBI; 

# SIGPIPE handler used to be here 

my $db = "redacted"; 
my $user = "redacted"; 
my $pass = "redacted"; 
my $table = "redacted"; 

my $ora = DBI->connect("dbi:Oracle:" . $db, $user, $pass); 
my $sql = "SELECT * FROM " . $table; 
my $query = $ora->prepare($sql); 
$query->execute(); 

$SIG{PIPE} = sub { print STDERR "WARNING: Received SIGPIPE"; exit(1); }; 

while (my @row = $query->fetchrow_array()) { 
    print(join('|', @row) . "\n"); 
} 

if ($DBI::err) { print STDERR "ERROR: Unload terminated due to error"; } 

我不知道是什麼原因它修復該問題,但它確實。

+0

更多關於我的理論的證據表明(Perl)db代碼設置了一個SIGPIPE處理程序(即使它不改變'$ SIG {PIPE}')。 – mob

0

可能是用於與數據庫通信的DBI驅動程序是用XS代碼編寫的。必須仔細編寫將要阻塞很長一段時間的XS代碼,以應付信號和perl的「安全信號」傳輸系統。您正在使用的數據庫驅動程序可能沒有考慮到這一點,因此不起作用。