线程卡在ssh连接中(perl)

时间:2013-09-30 13:23:39

标签: multithreading perl ssh

我正在研究一个创建线程的脚本,同时遍历一系列机器并检查事物。似乎当一个线程使用“ssh ......”进入它的独立终端时,它会卡住而我无法杀死它。他们还有一个似乎没有工作的计时器。

以下是代码:

sub call_cmd{
    my $host = shift;
    my $cmd = shift;
    my $command = $cmd;
    my $message;

    open( DIR, "$cmd|" ) || die "No cmd: $cmd $!";
    while(<DIR>){
        $message.=$_;
        print "\n $host $message \n";
    }
    close DIR;

    print "I'm here";
}

sub ssh_con($$){

my $host = $_[0]; 
my $cmd = "ssh $host -l $_[1]";
call_cmd($host,$cmd);

}

我收到ssh返回的输出消息,但我从未进入下一个打印。

这是创建线程的代码。

foreach(@machines){
    my $_ = threads->create(thread_creation,$_);
    $SIG{ALRM} = sub { $_->kill('ALRM') };
    push(@threads,$_);
}

sub thread_creation(){
    my $host =  $_;

    eval{
        $SIG{ALRM} = sub { die; };
        alarm(5);
        ssh_con($host,"pblue");
        alarm(0);
    }
}

输出:

 home/pblue> perl tsh.pl

 ssh XXXXX -l pblue

 ssh XXXXX -l pblue

  XXXXX Last login: Mon Sep 30 10:39:01 2013 from ldm052.wdf.sap.corp


  XXXXX Last login: Mon Sep 30 10:39:01 2013 from ldm052.wdf.sap.corp

1 个答案:

答案 0 :(得分:0)

除了您的代码有点奇怪之外,我遇到了您的问题 - 特别是在RHEL 5的Perl 5.8.8中。

似乎存在竞争条件,如果你同时在一个线程中产生两个ssh进程,它们会死锁。我找到的唯一解决方案是声明:

的解决方法
my $ssh_lock : shared;

然后'打开'你的ssh作为文件句柄:

my $ssh_data:
{
    lock ( $ssh_lock );
    open ( my $ssh_data, "|-", "ssh -n $hostname $command" ); 
}
#lock out of scope, so released
while ( <$ssh_data> ) {
    #do something
}

然而,对于更新版本的perl /更新版操作系统而言,这可能是一个有争议的问题。我当然无法特别可靠地重现它,当我开始使用fork()时,它完全消失了。

那就是说 - 你的代码做了一些相当奇怪的事情。尤其是您正在运行的命令是:

ssh $host -l pblue

这是一个有效的命令,但是它会以交互方式启动ssh - 但是因为你是多线程的,所以标准的in和stdout会做很奇怪的事情。

您应该对使用多线程的signal非常小心 - 由于进程间通信的性质,它不能很好地工作。设置ALARM信号

对于类似的事情 - 例如通过ssh运行命令 - 我用这样的方法取得了一定程度的成功:

#!/usr/bin/perl
use strict;
use warnings;

use threads;
use threads::shared;
use Thread::Queue;

my @servers_to_check = qw ( hostname1 hostname2 hostname3 hostname4 );

my $num_threads = 10;

my $task_q = Thread::Queue->new;
my $ssh_lock : shared;

sub worker_thread {
    my ($command_to_run) = @_;
    while ( my $server = $task_q->dequeue ) {
        my $ssh_results;
        {
            lock($ssh_lock);
            my $pid = open( $ssh_results, "-|",
                "ssh -n $server $command_to_run" );
        }
        while (<$ssh_results>) {
            print;
        }
        close($ssh_results);
    }
}

for ( 1 .. $num_threads ) {
    threads->create( \&worker_thread, "df -k" );
}

$task_q->enqueue(@servers_to_check);
$task_q->end;

foreach my $thr ( threads->list ) {
    $thr->join();
}