我正在研究一个创建线程的脚本,同时遍历一系列机器并检查事物。似乎当一个线程使用“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
答案 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();
}