我从主主机到从主机进行ZFS远程复制,其中我有一个在主主机上运行的Perl脚本。
对于每个文件系统,它ssh到远程主机并在侦听模式下启动mbuffer,然后脚本继续,然后发送数据。成功后,mbuffer应该自行退出。
问题
通过ssh在远程主机上启动mbuffer非常困难,然后能够在脚本中继续。我最终做了你在下面看到的内容。
问题是,在脚本退出之前,它会为每个文件系统留下<defunct>
个进程。
问题
可以避免<defunct>
进程吗?
sub mbuffer {
my ($id, $zfsPath) = @_;
my $m = join(' ', $mbuffer, '-I', $::c{port});
my $z = join(' ', $zfs, 'receive', , $zfsPath);
my $c = shellQuote($ssh, $::c{slaves}{$id}, join('|', $m, $z));
my $pm = Parallel::ForkManager->new(1);
my $pid = $pm->start;
if (!$pid) {
no warnings; # fixes "exec" not working
exec($c);
$pm->finish;
}
sleep 3; # wait for mbuffer to listen
return $pid;
}
答案 0 :(得分:3)
当你创建一个进程时,它会一直存在,直到它的父进程收到它为止。 (如果其父级首先退出,它将自动获得。)一个进程可以使用wait
或waitpid
来收集其子级。它还可以在创建孩子之前使用local $SIG{CHLD} = 'IGNORE';
自动收集孩子。
请注意,Parallel :: ForkManager不是启动单个子项的正确工具。产生单个工人并不是它的目的。
use String::ShellQuote qw( shell_quote );
sub mbuffer {
my ($id, $zfsPath) = @_;
my $mbuffer_cmd = shell_quote($mbuffer, '-I', $::c{port});
my $zfs_cmd = shell_quote($zfs, 'receive', $zfsPath);
my $remote_cmd = "$mbuffer_cmd | $zfs_cmd";
my $local_cmd = shell_quote($ssh, $::c{slaves}{$id}, $remote_cmd);
# open3 will close this handle.
# open3 doesn't deal well with lexical handles.
open(local *CHILD_STDIN, '<', '/dev/null') or die $!;
return open3('<&CHILD_STDIN', '>&STDOUT', '>&STDERR', $local_cmd);
}
IPC :: Open3级别很低,但它与您现有的代码最接近。更好的启动流程的方法包括IPC :: Run3和IPC :: Run。