结合Net :: OpenSSH和线程时的信号问题

时间:2014-04-08 08:08:56

标签: perl thread-safety signals openssh

我写了一个非常大的程序,在许多远程主机上执行命令,但我遇到了一个严重的问题,我不知道如何修复它。

经过大量的尝试后,我能够提取最少的代码,以便在我的机器上可靠地重现问题:

use warnings;
use strict;
use threads;
use threads::shared;
use Data::Dumper;
use POSIX ":sys_wait_h";
use Net::OpenSSH;
use Time::HiRes qw( usleep );

my @LIST=qw(host038b host039a host039b host040a host040b host041a host041b host043a
   host043b host044a host044b host045a host045b host046a host046b host047a host047b host049a
   host049b host050a host050b host054a host054b host055a host055b host056a host056b host057a
   host057b host058a host059a host059b host060a host060b host062a host062b host063a host068a
   host068b host069a host069b host071a host071b host072a host073a host073b host075a host075b
   host078a host078b host082a host082b host087a host087b host089a host089b host090a host090b
   host091a host091b host092a host092b host096a host096b host097a host097b host098a host099a
   host099b host100a);
my ($SSH, $CPID, %PIDS, @DONE);

sub _testthread {
  # Read stdout pipe
  my $SCROUT=shift;
  while (<$SCROUT>) {
    print $_;              # I normally write that to a logfile
  }
  return (0);
}

foreach (@LIST) {
$SSH->{$_}=Net::OpenSSH->new($_,       async => 1,
                                 master_opts => [ -o => "PasswordAuthentication=no"]);
}

$SIG{CHLD} = sub { my $WPID; 
            push (@DONE, { 'PID' => $WPID, 'RC' => $?, 'ERR' => $!}) while (($WPID = waitpid(-1, WNOHANG)) > 0) };

foreach (@LIST) {
  my ($SCRFH, $SCROUT, undef, $CPID) = $SSH->{$_}->open_ex({stdin_pipe => 1,
                                                           stdout_pipe => 1},  '/bin/bash -s');
  $PIDS{$CPID}='ACTIVE';
  threads->new('_testthread', $SCROUT);
  print $SCRFH "sleep 2\n";
  print $SCRFH "echo test `hostname`\n";
  print $SCRFH "exit 0\n";
  close $SCRFH;
  usleep 10000;
}

while (grep(/^ACTIVE/, values(%PIDS)) > 0) {
  print Dumper \%PIDS;
  while (@DONE) {
    my $DONE = shift (@DONE);
    $PIDS{$DONE->{PID}}='DONE';
  }
  sleep 1;
}

$_->join foreach (threads->list);

使用预先安装的perl 5.10,即使在取出一些将open_ex输出重新定位到文件描述符的更复杂的结构时,这段时间也会出现段错误。 使用新编译的perl 5.18.2,此脚本在大多数情况下无限期挂起,因为它似乎没有收到每个SIG {CHLD},即使我使用安全信令(据我所知)。

要重现此问题,以下事情似乎是必要的:

  • @LIST
  • 中有足够数量的主机
  • 让open_ex(或Net :: OpenSSH的派生方法)fork
  • 将该fork的STDOUT文件句柄赋予线程
  • 使用SIG {CHLD}的信号处理程序

由于我使用这种结构的更大程序几乎无法使用,如果有人能帮我找到解决方案,我会很高兴,也许是替代方案。

谢谢,问候,

Mazze

1 个答案:

答案 0 :(得分:1)

你正试图混合信号和线程,这总是一个坏主意。解决方案就是停止使用线程;无论你做什么,都可以通过不同的方式更好地完成。

也许可以考虑使用某种异步/事件驱动的IO系统来执行这些并发IO任务。