Perl 5.10 - POSIX 信号被忽略,除非在 sleep() 调用期间收到?

Posted

技术标签:

【中文标题】Perl 5.10 - POSIX 信号被忽略,除非在 sleep() 调用期间收到?【英文标题】:Perl 5.10 - POSIX Signals ignored unless received during sleep() call? 【发布时间】:2014-05-05 21:34:21 【问题描述】:

当谈到 Perl 如何处理 POSIX 信号时,我遇到了一些问题。也就是说,Perl 似乎会忽略这些信号,除非它们是在调用 sleep() 期间收到的。

例如,以下代码可以正常工作:

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

在另一个从 Oracle 数据库读取的脚本中使用上述 SIGPIPE 处理程序时,子例程似乎永远不会被调用。

#/usr/bin/perl
use DBI;

$SIGPIPE = 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"; 

我正在以相同的方式 (kill -sPIPE pid) 向两个脚本发送 SIGPIPE 信号,但只有第一个脚本响应它。第二个脚本继续进行。没有消息,没有退出,什么都没有。

我该如何纠正这种情况?

【问题讨论】:

无法复制。 Oracle驱动程序是否也有可能尝试使用$SIGPIPE?在调用execute 之前和之后检查$SIGPIPE 的值。 我在调用执行之前/之后得到相同的代码哈希。只是为了确保正在设置该值,我还让它在设置处理程序之前打印该值(它已按预期更新)。更复杂的是,在处理循环之前添加一个sleep(30) 可以让它捕获信号,但只能在睡眠期间。 读取不生成 SIGPIPE;当您尝试写入没有接收器进程的管道或套接字时,您会得到 SIGPIPE。 请注意,信号处理程序仅在 Perl 指令之间调用。如果在执行 Perl 指令期间进入(包括对 C 数据库驱动程序库函数的调用),则将在指令完成后调用信号处理程序。 【参考方案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();

$SIGPIPE = 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"; 

我不确定为什么它可以解决问题,但确实可以。

【讨论】:

我的理论的更多证据表明 (Perl) db 代码设置了一个 SIGPIPE 处理程序(即使它不会更改 $SIGPIPE)。【参考方案2】:

用于与数据库通信的 DBI 驱动程序很可能是用 XS 代码编写的。将要长时间阻塞的 XS 代码必须仔细编写以应对信号和 perl 的“安全信号”传递系统。您使用的 DB 驱动程序可能没有考虑到这一点,因此无法正常工作。

【讨论】:

以上是关于Perl 5.10 - POSIX 信号被忽略,除非在 sleep() 调用期间收到?的主要内容,如果未能解决你的问题,请参考以下文章

perl 5.8 和 5.10 之间的区别 [关闭]

高效 pre-perl-5.10 等效于 pack("Q>")

在linux下的如何将perl默认版本5.8.8升级为5.10

UNP学习第五章

Perl 5.10 是不是把原型弄乱了?

为啥我的继续执行 Perl 5.10 中的下一个 when 块?